The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .github
    └── workflows
    │   └── ci.yml
├── .gitignore
├── LICENSE
├── LICENSE_THIRDPARTY
├── README.md
├── benchmarks
    ├── computations-1.ins
    ├── computations-160.ins
    └── run
├── copy.js
├── docs
    ├── development.md
    ├── faq.md
    ├── features.md
    ├── generate
    ├── manpage-footer.md
    ├── manpage-header.md
    ├── pros-and-cons.md
    ├── reference-syntax.md
    ├── reference-units.md
    ├── template.readme
    └── terminal-version.md
├── generate-third-party-licenses.cjs
├── index.dev.js
├── insect.desktop
├── npm-shrinkwrap.json
├── package.json
├── packages.dhall
├── spago.dhall
├── src
    ├── Insect.purs
    └── Insect
    │   ├── Environment.purs
    │   ├── Format.purs
    │   ├── Functions.purs
    │   ├── Interpreter.purs
    │   ├── Language.purs
    │   ├── Parser.purs
    │   └── PrettyPrint.purs
├── test.dhall
├── test
    └── Main.purs
└── web
    ├── .htaccess
    ├── index.html
    ├── main.css
    ├── media
        ├── README.md
        ├── insect-16x16.png
        ├── insect-196x196.png
        ├── insect-32x32.png
        ├── insect-banner.png
        ├── insect-banner.svg
        ├── insect.png
        └── insect.svg
    ├── opensearch.xml
    └── third-party-licenses.txt


/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
 1 | name: CI
 2 | 
 3 | on:
 4 |   push:
 5 |     branches: [master]
 6 |   pull_request:
 7 |     branches: [master]
 8 | 
 9 | permissions:
10 |   contents: read
11 | 
12 | jobs:
13 |   build:
14 |     runs-on: ubuntu-latest
15 |     steps:
16 |       - uses: actions/checkout@v3
17 | 
18 |       - name: Cache PureScript dependencies
19 |         uses: actions/cache@v3
20 |         with:
21 |           key: ${{ runner.os }}-spago-${{ hashFiles('**/*.dhall') }}
22 |           path: |
23 |             .spago
24 |             output
25 | 
26 |       - uses: actions/setup-node@v3
27 |         with:
28 |           node-version: "lts/*"
29 | 
30 |       - name: Install dependencies
31 |         run: npm ci
32 | 
33 |       - name: Build Insect
34 |         run: npm run build
35 | 
36 |       - name: Check that index.cjs runs successfully
37 |         run: ./index.cjs '1 + 1'
38 | 
39 |       - name: Run tests
40 |         run: npm test
41 | 
42 |       - name: Generate README
43 |         uses: docker://pandoc/core:3.1
44 |         with:
45 |           entrypoint: ./docs/generate
46 | 
47 |       - name: Check generated README is up to date
48 |         run: |
49 |           if [ -n "$(git status --porcelain README.md)" ]; then
50 |             echo 'There is a mismatch between the autogenerated `README.md` file and the source files in the `docs` directory. If you have edited `README.md` directly, please modify the files in `docs` instead, then run `./docs/generate`. If you already modified the files in `docs`, please run `./docs/generate`.'
51 |             echo 'Note that you need to have Pandoc (https://pandoc.org) installed to run `./docs/generate`. You can find the Pandoc version used by Insect in `.github/workflows/ci.yml` (note that the patch version is intentionally not specified in the workflow file, but it'\''s okay for you to have it).'
52 |             echo
53 |             echo "Here's the diff:"
54 |             git diff README.md
55 |             exit 1
56 |           fi
57 | 
58 |       - name: Generate third-party licenses
59 |         run: ./generate-third-party-licenses.cjs
60 | 
61 |       - name: Check generated third-party licenses list is up to date
62 |         run: |
63 |           if [ -n "$(git status --porcelain LICENSE_THIRDPARTY web/third-party-licenses.txt)" ]; then
64 |             echo 'There is a mismatch between the autogenerated third-party license list and what running `generate-third-party-licenses.cjs` generates. If you have edited LICENSE_THIRDPARTY or web/third-party-licenses.txt directly, please revert those changes. Otherwise, please run `node generate-third-party-licenses.cjs` locally and commit the changes to the license list to Git.'
65 |             exit 1
66 |           fi
67 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | /node_modules/
 2 | /output/
 3 | /.spago/
 4 | /.psc*
 5 | /.psa*
 6 | /index.cjs
 7 | /docs/insect.1
 8 | /web/*.js
 9 | /web/terminal.css
10 | /benchmarks/results/
11 | /benchmarks/results.json
12 | /benchmarks/results.md
13 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2018 David Peter
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
  1 | ## Note: Please consider using the follow-up project instead: [Numbat](https://github.com/sharkdp/numbat).
  2 | 
  3 | you can read more about why Insect has been rewritten from scratch [here](https://github.com/sharkdp/numbat/blob/master/assets/reasons-for-rewriting-in-rust.md).
  4 | 
  5 | ---
  6 | 
  7 | <!--
  8 | 
  9 |     --- IMPORTANT --- IMPORTANT --- IMPORTANT ---
 10 | 
 11 |      Do not edit this README file directly. Edit
 12 |      the files in the `docs` folder instead.
 13 | 
 14 |     --- IMPORTANT --- IMPORTANT --- IMPORTANT ---
 15 | 
 16 | -->
 17 | 
 18 | ![insect](web/media/insect.png "insect - scientific calculator")
 19 | 
 20 | A high-precision scientific calculator with full support for physical units.
 21 | 
 22 | Contents
 23 | --------
 24 | 
 25 | - [Documentation](#documentation)
 26 | - [Reference](#reference)
 27 | - [Pros and cons](#pros-and-cons)
 28 | - [FAQ](#faq)
 29 | - [Terminal version](#terminal-version)
 30 | - [Development](#development)
 31 | - [Maintainers](#maintainers)
 32 | 
 33 | ## Documentation
 34 | 
 35 | - **Evaluate mathematical expressions**:
 36 | 
 37 |       1920/16*9
 38 |       2^32
 39 |       sqrt(1.4^2 + 1.5^2) * cos(pi/3)^2
 40 | 
 41 |   - **Operators**: addition (`+`), subtraction (`-`), multiplication
 42 |     (`*`, `·`, `×`), division (`/`, `÷`, `per`), exponentiation (`^`,
 43 |     `**`). Full list: see [*Reference*](#reference) below.
 44 | 
 45 |   - **Mathematical functions**: `abs`, `acos`, `acosh`,
 46 |     `acot`/`arccotangent`, `acoth`/`archypcotangent`,
 47 |     `acsc`/`arccosecant`, `acsch`/`archypcosecant`, `arcsecant`,
 48 |     `asech`/`archypsecant`, `asin`, `asinh`, `atan2`, `atan`, `atanh`,
 49 |     `ceil`, `cos`, `cosh`, `cot`/`cotangent`, `coth`/`hypcotangent`,
 50 |     `csc`/`cosecant`, `csch`/`hypcosecant`, `exp`, `floor`,
 51 |     `fromCelsius`, `fromFahrenheit`, `gamma`, `ln`, `log10`, `log`,
 52 |     `maximum`, `mean`, `minimum`, `round`, `secant`, `sech`/`hypsecant`,
 53 |     `sin`, `sinh`, `sqrt`, `tan`, `tanh`, `toCelsius`, `toFahrenheit`.
 54 | 
 55 |   - **High-precision numeric type** with *30* significant digits that
 56 |     can handle *very* large (or small) exponents like *10^(10^10)*.
 57 | 
 58 |   - **Exponential notation**: `6.022e23`.
 59 | 
 60 |   - **Hexadecimal, octal and binary number input**:
 61 | 
 62 |         0xFFFF
 63 |         0b1011
 64 |         0o32
 65 |         0x2.F
 66 |         0o5p3
 67 | 
 68 | - **Physical units**: parsing and handling, including metric prefixes:
 69 | 
 70 |       2 min + 30 s
 71 |       40 kg * 9.8 m/s^2 * 150 cm
 72 |       sin(30°)
 73 | 
 74 |   - **Supported units**: see [*Reference*](#reference) section below.
 75 | 
 76 |   - **Implicit conversions**: `15 km/h * 30 min` evaluates to `7.5 km`.
 77 | 
 78 |   - **Useful error messages**:
 79 | 
 80 |         > 2 watts + 4 newton meter
 81 | 
 82 |         Conversion error:
 83 |           Cannot convert unit N·m (base units: kg·m²·s⁻²)
 84 |                       to unit W (base units: kg·m²·s⁻³)
 85 | 
 86 | - **Explicit unit conversions**: the `->` conversion operator (aliases:
 87 |   `→`, `➞`, `to`):
 88 | 
 89 |       60 mph -> m/s
 90 |       500 km/day -> km/h
 91 |       1 mrad -> degree
 92 |       52 weeks -> days
 93 |       5 in + 2 ft -> cm
 94 |       atan(30 cm / 2 m) -> degree
 95 |       6 Mbit/s * 1.5 h -> GB
 96 | 
 97 | - **Variable assignments**:
 98 | 
 99 |   Example: mass of the earth
100 | 
101 |       r = 6000km
102 |       vol = 4/3 * pi * r^3
103 |       density = 5 g/cm^3
104 |       vol * density -> kg
105 | 
106 |   Example: oscillation period of a pendulum
107 | 
108 |       len = 20 cm
109 |       2pi*sqrt(len/g0) -> ms
110 | 
111 |   - **Predefined constants** (type `list` to see them all): speed of
112 |     light (`c`), Planck's constant (`h_bar`), electron mass
113 |     (`electronMass`), elementary charge (`elementaryCharge`), magnetic
114 |     constant (`µ0`), electric constant (`eps0`), Bohr magneton (`µ_B`),
115 |     Avogadro's constant (`N_A`), Boltzmann constant (`k_B`),
116 |     gravitational acceleration (`g0`), ideal gas constant (`R`), ...
117 | 
118 |   - **Last result**: you can use `ans` (answer) or `_` to refer to the
119 |     result of the last calculation.
120 | 
121 | - **User-defined functions**:
122 | 
123 |   Example: kinetic energy
124 | 
125 |       kineticEnergy(mass, speed) = 0.5 * mass * speed^2 -> kJ
126 | 
127 |       kineticEnergy(800 kg, 120 km/h)
128 | 
129 |   Example: barometric formula
130 | 
131 |       P0 = 1 atm
132 |       T0 = fromCelsius(15)
133 |       tempGradient = 0.65 K / 100 m
134 | 
135 |       pressure(height) = P0 * (1 - tempGradient * height / T0)^5.255 -> hPa
136 | 
137 |       pressure(1500 m)
138 | 
139 | - **Sums and products**:
140 | 
141 |   Syntax:
142 | 
143 |       sum(<expression>, <index-variable>, <from>, <to>)
144 |       product(<expression>, <index-variable>, <from>, <to>)
145 | 
146 |   Examples:
147 | 
148 |       # sum of the first ten squares
149 |       sum(k^2, k, 1, 10)
150 | 
151 |       # the factorial of n as the product 1 × 2 × ... × n
152 |       myFactorial(n) = product(k, k, 1, n)
153 | 
154 | - **Unicode support**:
155 | 
156 |       λ = 2 × 300 µm
157 |       ν = c/λ → GHz
158 | 
159 | - **And more**: tab completion, command history (arrow keys,
160 |   `Ctrl`+`R`), pretty printing, syntax highlighting, ...
161 | 
162 | ## Reference
163 | 
164 | - Operators (ordered by precedence: high to low)
165 | 
166 |   | Operator                  | Syntax               |
167 |   |---------------------------|----------------------|
168 |   | factorial                 | `!`                  |
169 |   | square, cube, ...         | `²`, `³`, `⁻¹`, ...  |
170 |   | exponentiation            | `^`, `**`            |
171 |   | multiplication (implicit) | *whitespace*         |
172 |   | modulo                    | `%`                  |
173 |   | division                  | `per`                |
174 |   | division                  | `/`, `÷`             |
175 |   | multiplication (explicit) | `*`, `·`, `×`        |
176 |   | subtraction               | `-`                  |
177 |   | addition                  | `+`                  |
178 |   | unit conversion           | `->`, `→`, `➞`, `to` |
179 |   | assignment                | `=`                  |
180 | 
181 |   Note that *implicit* multiplication has a higher precedence than
182 |   division, i.e. `50 cm / 2 m` will be parsed as `50 cm / (2 m)`.
183 | 
184 | - Commands
185 | 
186 |   | Command                  | Syntax             |
187 |   |--------------------------|--------------------|
188 |   | help text                | `help`, `?`        |
189 |   | list of variables        | `list`, `ls`, `ll` |
190 |   | reset environment        | `reset`            |
191 |   | clear screen             | `clear`, `cls`     |
192 |   | copy result to clipboard | `copy`, `cp`       |
193 |   | quit (CLI)               | `quit`, `exit`     |
194 | 
195 | - Supported units (remember that you can use tab completion).
196 | 
197 |   All SI-accepted units support metric prefixes and [binary
198 |   prefixes](https://en.wikipedia.org/wiki/Binary_prefix) (`MiB`, `GiB`,
199 |   ...).
200 | 
201 |   | Unit                                                                         | Syntax                                                                      |
202 |   |------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
203 |   | [Ampere](https://en.wikipedia.org/wiki/Ampere)                               | `amperes`, `ampere`, `A`                                                    |
204 |   | [Ångström](https://en.wikipedia.org/wiki/Ångström)                           | `angstroms`, `angstrom`, `Å`                                                |
205 |   | [Astronomical unit](https://en.wikipedia.org/wiki/Astronomical_unit)         | `AU`, `au`, `astronomicalunits`, `astronomicalunit`                         |
206 |   | [Atmosphere](https://en.wikipedia.org/wiki/Atmosphere_(unit))                | `atm`                                                                       |
207 |   | [Bar](https://en.wikipedia.org/wiki/Bar_(unit))                              | `bars`, `bar`                                                               |
208 |   | [Barn](https://en.wikipedia.org/wiki/Barn_(unit))                            | `barns`, `barn`                                                             |
209 |   | [Becquerel](https://en.wikipedia.org/wiki/Becquerel)                         | `becquerels`, `becquerel`, `Bq`                                             |
210 |   | [Bel](https://en.wikipedia.org/wiki/Decibel)                                 | `bels`, `bel`                                                               |
211 |   | [Bit](https://en.wikipedia.org/wiki/Bit)                                     | `bits`, `bit`                                                               |
212 |   | [Bits per second](https://en.wikipedia.org/wiki/Data_rate_units)             | `bps`                                                                       |
213 |   | [British thermal unit](https://en.wikipedia.org/wiki/British_thermal_unit)   | `BTU`                                                                       |
214 |   | [Byte](https://en.wikipedia.org/wiki/Byte)                                   | `Bytes`, `bytes`, `Byte`, `byte`, `B`, `Octets`, `octets`, `Octet`, `octet` |
215 |   | [Calorie](https://en.wikipedia.org/wiki/Calorie)                             | `calories`, `calorie`, `cal`                                                |
216 |   | [Candela](https://en.wikipedia.org/wiki/Candela)                             | `candelas`, `candela`, `cd`                                                 |
217 |   | [Coulomb](https://en.wikipedia.org/wiki/Coulomb)                             | `coulombs`, `coulomb`, `C`                                                  |
218 |   | [Cup](https://en.wikipedia.org/wiki/Cup_(unit))                              | `cups`, `cup`                                                               |
219 |   | [DPI](https://en.wikipedia.org/wiki/Dots_per_inch)                           | `dpi`                                                                       |
220 |   | [Day](https://en.wikipedia.org/wiki/Day)                                     | `days`, `day`, `d`                                                          |
221 |   | [Degree](https://en.wikipedia.org/wiki/Degree_(angle))                       | `degrees`, `degree`, `deg`, `°`                                             |
222 |   | [Dot](https://en.wikipedia.org/wiki/Dots_per_inch)                           | `dots`, `dot`                                                               |
223 |   | [Electronvolt](https://en.wikipedia.org/wiki/Electronvolt)                   | `electronvolts`, `electronvolt`, `eV`                                       |
224 |   | [Euro](https://en.wikipedia.org/wiki/Euro)                                   | `euros`, `euro`, `EUR`, `€`                                                 |
225 |   | [Farad](https://en.wikipedia.org/wiki/Farad)                                 | `farads`, `farad`, `F`                                                      |
226 |   | [Fluid ounce](https://en.wikipedia.org/wiki/Fluid_ounce)                     | `fluidounces`, `fluidounce`, `floz`                                         |
227 |   | [Foot](https://en.wikipedia.org/wiki/Foot_(unit))                            | `feet`, `foot`, `ft`                                                        |
228 |   | [Fortnight](https://en.wikipedia.org/wiki/Fortnight)                         | `fortnights`, `fortnight`                                                   |
229 |   | [Frame](https://en.wikipedia.org/wiki/Film_frame)                            | `frames`, `frame`                                                           |
230 |   | [Frames per second](https://en.wikipedia.org/wiki/Frame_rate)                | `fps`                                                                       |
231 |   | [Furlong](https://en.wikipedia.org/wiki/Furlong)                             | `furlongs`, `furlong`                                                       |
232 |   | [Gallon](https://en.wikipedia.org/wiki/Gallon)                               | `gallons`, `gallon`, `gal`                                                  |
233 |   | [Gauss](https://en.wikipedia.org/wiki/Gauss_(unit))                          | `gauss`                                                                     |
234 |   | [Gram](https://en.wikipedia.org/wiki/Gram)                                   | `grams`, `gram`, `grammes`, `gramme`, `g`                                   |
235 |   | [Gray](https://en.wikipedia.org/wiki/Gray_(unit))                            | `grays`, `gray`, `Gy`                                                       |
236 |   | [Hectare](https://en.wikipedia.org/wiki/Hectare)                             | `hectares`, `hectare`, `ha`                                                 |
237 |   | [Henry](https://en.wikipedia.org/wiki/Henry_(unit))                          | `henrys`, `henries`, `henry`, `H`                                           |
238 |   | [Hertz](https://en.wikipedia.org/wiki/Hertz)                                 | `hertz`, `Hz`                                                               |
239 |   | [Hogshead](https://en.wikipedia.org/wiki/Hogshead)                           | `hogsheads`, `hogshead`                                                     |
240 |   | [Hour](https://en.wikipedia.org/wiki/Hour)                                   | `hours`, `hour`, `hr`, `h`                                                  |
241 |   | [Inch](https://en.wikipedia.org/wiki/Inch)                                   | `inches`, `inch`, `in`                                                      |
242 |   | [Joule](https://en.wikipedia.org/wiki/Joule)                                 | `joules`, `joule`, `J`                                                      |
243 |   | [Katal](https://en.wikipedia.org/wiki/Katal)                                 | `katals`, `katal`, `kat`                                                    |
244 |   | [Knot](https://en.wikipedia.org/wiki/Knot_(unit))                            | `knots`, `knot`, `kn`, `kt`                                                 |
245 |   | [Kelvin](https://en.wikipedia.org/wiki/Kelvin)                               | `kelvins`, `kelvin`, `K`                                                    |
246 |   | [Light-year](https://en.wikipedia.org/wiki/Light-year)                       | `lightyears`, `lightyear`, `ly`                                             |
247 |   | [Liter](https://en.wikipedia.org/wiki/Liter)                                 | `liters`, `liter`, `litres`, `litre`, `L`, `l`                              |
248 |   | [Lumen](https://en.wikipedia.org/wiki/Lumen_(unit))                          | `lumens`, `lumen`, `lm`                                                     |
249 |   | [Lux](https://en.wikipedia.org/wiki/Lux)                                     | `lux`, `lx`                                                                 |
250 |   | [Meter](https://en.wikipedia.org/wiki/Meter)                                 | `meters`, `meter`, `metres`, `metre`, `m`                                   |
251 |   | [Mile](https://en.wikipedia.org/wiki/Mile)                                   | `miles`, `mile`                                                             |
252 |   | [Miles per hour](https://en.wikipedia.org/wiki/Miles_per_hour)               | `mph`                                                                       |
253 |   | [Millimeter of mercury](https://en.wikipedia.org/wiki/Millimeter_of_mercury) | `mmHg`                                                                      |
254 |   | [Minute](https://en.wikipedia.org/wiki/Minute)                               | `minutes`, `minute`, `min`                                                  |
255 |   | [Molal](https://en.wikipedia.org/wiki/Molality#Unit)                         | `molals`, `molal`                                                           |
256 |   | [Molar](https://en.wikipedia.org/wiki/Molar_concentration#Units)             | `molars`, `molar`                                                           |
257 |   | [Mole](https://en.wikipedia.org/wiki/Mole_(unit))                            | `moles`, `mole`, `mol`                                                      |
258 |   | [Month](https://en.wikipedia.org/wiki/Month)                                 | `months`, `month`                                                           |
259 |   | [Nautical mile](https://en.wikipedia.org/wiki/Nautical_mile)                 | `M`, `NM`, `nmi`                                                            |
260 |   | [Newton](https://en.wikipedia.org/wiki/Newton_(unit))                        | `newtons`, `newton`, `N`                                                    |
261 |   | [Ohm](https://en.wikipedia.org/wiki/Ohm)                                     | `ohms`, `ohm`, `Ω`                                                          |
262 |   | [Ounce](https://en.wikipedia.org/wiki/Ounce)                                 | `ounces`, `ounce`, `oz`                                                     |
263 |   | [PPI](https://en.wikipedia.org/wiki/Pixels_per_inch)                         | `ppi`                                                                       |
264 |   | [Parsec](https://en.wikipedia.org/wiki/Parsec)                               | `parsecs`, `parsec`, `pc`                                                   |
265 |   | [Parts-per-million](https://en.wikipedia.org/wiki/Parts-per_notation)        | `ppm`                                                                       |
266 |   | [Parts-per-billion](https://en.wikipedia.org/wiki/Parts-per_notation)        | `ppb`                                                                       |
267 |   | [Parts-per-trillion](https://en.wikipedia.org/wiki/Parts-per_notation)       | `ppt`                                                                       |
268 |   | [Parts-per-quadrillion](https://en.wikipedia.org/wiki/Parts-per_notation)    | `ppq`                                                                       |
269 |   | [Pascal](https://en.wikipedia.org/wiki/Pascal_(unit))                        | `pascals`, `pascal`, `Pa`                                                   |
270 |   | [Percent](https://en.wikipedia.org/wiki/Parts-per_notation)                  | `percent`, `pct`                                                            |
271 |   | [Person](https://en.wiktionary.org/wiki/person)                              | `persons`, `person`, `people`                                               |
272 |   | [Piece](https://en.wiktionary.org/wiki/piece)                                | `pieces`, `piece`                                                           |
273 |   | [Pint](https://en.wikipedia.org/wiki/Pint)                                   | `pints`, `pint`                                                             |
274 |   | [Pixel](https://en.wikipedia.org/wiki/Pixel)                                 | `pixels`, `pixel`, `px`                                                     |
275 |   | [Pound-force](https://en.wikipedia.org/wiki/Pound_%28force%29)               | `pound_force`, `lbf`                                                        |
276 |   | [Pound](https://en.wikipedia.org/wiki/Pound_(mass))                          | `pounds`, `pound`, `lb`                                                     |
277 |   | [Psi](https://en.wikipedia.org/wiki/Pounds_per_square_inch)                  | `psi`                                                                       |
278 |   | [RPM](https://en.wikipedia.org/wiki/RPM)                                     | `RPM`, `rpm`                                                                |
279 |   | [Radian](https://en.wikipedia.org/wiki/Radian)                               | `radians`, `radian`, `rad`                                                  |
280 |   | [Rod](https://en.wikipedia.org/wiki/Rod_(unit))                              | `rods`, `rod`                                                               |
281 |   | [Second](https://en.wikipedia.org/wiki/Second)                               | `seconds`, `second`, `sec`, `s`                                             |
282 |   | [Siemens](https://en.wikipedia.org/wiki/Siemens_(unit))                      | `siemens`, `S`                                                              |
283 |   | [Sievert](https://en.wikipedia.org/wiki/Sievert)                             | `sieverts`, `sievert`, `Sv`                                                 |
284 |   | [Tablespoon](https://en.wikipedia.org/wiki/Tablespoon)                       | `tablespoons`, `tablespoon`, `tbsp`                                         |
285 |   | [Teaspoon](https://en.wikipedia.org/wiki/Teaspoon)                           | `teaspoons`, `teaspoon`, `tsp`                                              |
286 |   | [Tesla](https://en.wikipedia.org/wiki/Tesla_(unit))                          | `teslas`, `tesla`, `T`                                                      |
287 |   | [Thou](https://en.wikipedia.org/wiki/Thousandth_of_an_inch)                  | `thou`, `mils`, `mil`                                                       |
288 |   | [Tonne](https://en.wikipedia.org/wiki/Tonne)                                 | `tonnes`, `tonne`, `tons`, `ton`, `t`                                       |
289 |   | [US Dollar](https://en.wikipedia.org/wiki/USD)                               | `dollars`, `dollar`, `USD`, `



    
    

    
    

    
    
    
    

    
    
    
    




    

    
The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
| 290 | | [Volt](https://en.wikipedia.org/wiki/Volt) | `volts`, `volt`, `V` | 291 | | [Watt-hour](https://en.wikipedia.org/wiki/Kilowatt_hour) | `Wh` | 292 | | [Watt](https://en.wikipedia.org/wiki/Watt) | `watts`, `watt`, `W` | 293 | | [Weber](https://en.wikipedia.org/wiki/Weber_(unit)) | `webers`, `weber`, `Wb` | 294 | | [Week](https://en.wikipedia.org/wiki/Week) | `weeks`, `week` | 295 | | [Yard](https://en.wikipedia.org/wiki/Yard) | `yards`, `yard`, `yd` | 296 | | [Gregorian year](https://en.wikipedia.org/wiki/Gregorian_year) | `years`, `year` | 297 | | [Julian year](https://en.wikipedia.org/wiki/Julian_year_(astronomy)) | `julianYears`, `julianYear` | 298 | 299 | ## Pros and cons 300 | 301 | **Reasons to use Insect** 302 | 303 | - Insect is **open-source**. 304 | - There is a [web version](https://Insect.sh/) that requires **no 305 | installation**. 306 | - With both browser and terminal versions available, Insect is truly 307 | **cross-platform**. 308 | - Insect has *first-class* support for **physical units**, including 309 | metric and binary prefixes. While evaluating your calculation, Insect 310 | ensures that you did not accidentally make any mistakes in combining 311 | the physical quantities. 312 | - Insect supports an 313 | [**interactive**](https://en.wikipedia.org/wiki/REPL) style with its 314 | readline-like interface. There is a saved history that can be browsed 315 | by pressing the up and down arrow keys. The history is also searchable 316 | via *Ctrl-R*. 317 | - Insect's syntax is rather strict. The parser does not try to be 318 | "smart" on syntactically incorrect input, so there shouldn't be any 319 | surprises - and you can trust the result of your calculation. The 320 | parsed user input is always pretty-printed for a quick double-check. 321 | - Insect is written in [PureScript](http://www.purescript.org/) and 322 | therefore benefits from all the safety guarantees that a strictly 323 | typed functional programming language gives you. 324 | - The source code of 325 | [purescript-quantities](https://github.com/sharkdp/purescript-quantities) 326 | (the underlying library for physical units) as well as the code of 327 | Insect itself is **extensively tested**. 328 | 329 | **Reasons to choose an alternative** 330 | 331 | - Insect is a scientific calculator. It's not a computer algebra system 332 | that solves differential equations or computes integrals. Try 333 | *[WolframAlpha](http://www.wolframalpha.com/)* instead. 334 | - There is no graphical user interface with buttons for each action 335 | (*x²*, *1/x*, *DEG/RAD*, etc.). 336 | *[Qalculate!](http://qalculate.github.io/)* is a fantastic tool that 337 | supports both text as well as graphical input. 338 | - Insect supports a huge range of physical units: all [SI 339 | units](https://en.wikipedia.org/wiki/International_System_of_Units), 340 | all non-SI units that are accepted by SI as well as most units of the 341 | imperial and US customary systems (and many more). However, if you 342 | need something even more comprehensive, try *[GNU 343 | units](https://www.gnu.org/software/units/)*. 344 | - Insect is not a general-purpose programming language. You could try 345 | *[Frink](https://frinklang.org/)*. 346 | - Insect does not have a special mode for hexadecimal, octal, or binary 347 | numbers (yet), though it does support inputting them. 348 | 349 | ## FAQ 350 | 351 | - Why are Celsius and Fahrenheit not supported? 352 | 353 | In contrast to the SI unit of temperature, the 354 | [Kelvin](https://en.wikipedia.org/wiki/Kelvin), and to all other 355 | units, Celsius and Fahrenheit both require an additive offset when 356 | converting into and from other temperature units. This additive offset 357 | leads to all kinds of ambiguities when performing calculations in 358 | these units. Adding two temperatures in Celsius, for example, is only 359 | meaningful if one of them is seen as an offset value (rather than as 360 | an absolute temperature). Insect is primarily a scientific calculator 361 | (as opposed to a unit conversion tool) and therefore focuses on 362 | getting physical calculations right. 363 | 364 | Even though *°C* and *°F* are not supported as built-in units, there 365 | are helper functions to convert to and from Celsius (and Fahrenheit): 366 | 367 | - `fromCelsius` takes a **scalar value** that represents a temperature 368 | in Celsius and returns a corresponding **temperature in Kelvin**: 369 | 370 | > fromCelsius(0) 371 | 372 | = 273.15 K 373 | 374 | > k_B * fromCelsius(23) to meV 375 | 376 | = 25.5202 meV 377 | 378 | - `toCelsius` takes a **temperature in Kelvin** and returns a **scalar 379 | value** that represents the corresponding temperature in Celsius: 380 | 381 | > toCelsius(70 K) 382 | 383 | = -203.15 384 | 385 | > toCelsius(25 meV / k_B) 386 | 387 | = 16.963 388 | 389 | - Why is `1/2 x` parsed as `1/(2x)`? 390 | 391 | *Implicit* multiplication (without an explicit multiplication sign) 392 | has a higher precedence than division (see [operator precedence 393 | rules](#reference)). This is by design, in order to parse inputs like 394 | `50 cm / 2 m` as `(50 cm) / (2 m)`. If you meant *½ · x*, write 395 | `1/2 * x`. 396 | 397 | - What is the internal numerical precision? 398 | 399 | By default, Insect shows 6 significant digits in the result of the 400 | calculation. However, the internal numerical precision is much higher 401 | (30 digits). 402 | 403 | - How does the conversion operator work? 404 | 405 | The conversion operator `->` attempts to convert the physical quantity 406 | on its left hand side to the *unit of the expression* on its right 407 | hand side. This means that you can write an arbitrary expression on 408 | the right hand side (but only the unit part will be extracted). For 409 | example: 410 | 411 | # simple unit conversion: 412 | > 120 km/h -> mph 413 | 414 | = 74.5645 mi/h 415 | 416 | # expression on the right hand side: 417 | > 120 m^3 -> km * m^2 418 | 419 | = 0.12 m²·km 420 | 421 | # convert x1 to the same unit as x2: 422 | > x1 = 50 km / h 423 | > x2 = 3 m/s -> x1 424 | 425 | x2 = 10.8 km/h 426 | 427 | - What is the relation between the units `RPM`, `rad/s`, `deg/s` and 428 | `Hz`? 429 | 430 | The unit [`RPM`](https://en.wikipedia.org/wiki/Revolutions_per_minute) 431 | (revolutions per minute) is defined via `1 RPM = 1 / minute` where the 432 | `1` on the right hand side symbolizes "1 revolution". 433 | 434 | As the base unit is the same (`1 / second`), `RPM` can be converted to 435 | `rad / s`, `deg / s` or `Hz`. Note, however, that `1 RPM` does *not* 436 | equal `2π rad / min` or `360° / min` or `1 Hz`, as some might expect. 437 | If you're interested in computing the traversed angle of something 438 | that rotates with a given number of revolutions per minute, you need 439 | to multiply by `2π rad` or `360°` because: 440 | 441 | 1 RPM · (360°/revolution) = (1 revolution / minute) · (360° / revolution) = 360° / minute 442 | 443 | ## Terminal version 444 | 445 | In addition to the web interface, there is also a command-line version 446 | (supporting Node.js 10 and later) which can by installed via 447 | [npm](https://www.npmjs.com/package/insect): 448 | 449 | npm install -g insect 450 | 451 | Note that you should almost always never run this as root or with 452 | `sudo`. If the command fails due to permission issues, [set up a prefix 453 | directory](https://github.com/sindresorhus/guides/blob/master/npm-global-without-sudo.md#install-npm-packages-globally-without-sudo-on-macos-and-linux) 454 | and call `npm install` as a user instead. 455 | 456 | For Arch Linux, there is an [AUR 457 | package](https://aur.archlinux.org/packages/insect/): 458 | 459 | yaourt -S insect 460 | 461 | For macOS, there is a [Homebrew 462 | formula](https://formulae.brew.sh/formula/insect): 463 | 464 | brew install insect 465 | 466 | For Android, install [Termux](https://termux.com/) from 467 | [F-Droid](https://f-droid.org/packages/com.termux/). Install Node.js in 468 | Termux and then install `insect` from npm: 469 | 470 | pkg install nodejs-lts 471 | npm install -g insect 472 | 473 | ## Development 474 | 475 | Insect is written in PureScript (see the [Getting 476 | Started](https://github.com/purescript/documentation/blob/master/guides/Getting-Started.md) 477 | guide). First, install all dependencies: 478 | 479 | npm install 480 | 481 | To start the web version: 482 | 483 | npm start 484 | 485 | To build a bundled JavaScript file that you can run from the terminal 486 | (note that this builds the web version too): 487 | 488 | npm run build 489 | 490 | To run the `index.cjs` file which the previous command creates: 491 | 492 | node index.cjs 493 | # Or simply on Un*x 494 | ./index.cjs 495 | 496 | Note that it's not possible to just move this file anywhere and then run 497 | it there, since it depends on packages in `node_modules`. 498 | 499 | Insect comes with a comprehensive set of [unit tests](test/Main.purs). 500 | To run them: 501 | 502 | npm test 503 | 504 | Note that Node.js 12 or above is required to work on/build Insect 505 | (despite Insect itself requiring only Node.js 10 or later to run). If 506 | you don't have or want to install Node.js 12 or later, you can use the 507 | following Dockerfile to build or run Insect on Node.js 18: 508 | 509 | ``` Dockerfile 510 | FROM node:18 511 | 512 | WORKDIR /usr/src/insect 513 | 514 | COPY . . 515 | 516 | RUN npm install && \ 517 | npm run build 518 | 519 | CMD ["node", "index.cjs"] 520 | ``` 521 | 522 | After creating the image (`docker build -t sharkdp/insect .`), you can 523 | create the container and copy out the build artifacts: 524 | 525 | docker create sharkdp/insect:latest 526 | # copy SHA (e.g. 71f0797703e8) 527 | docker cp 71f0797703e8:/usr/src/insect/index.cjs . 528 | docker cp -r 71f0797703e8:/usr/src/insect/node_modules . 529 | 530 | To directly run Insect inside Docker (paying a heavy startup time 531 | penalty), you can use: 532 | 533 | docker run -it --rm -v ~/.local/share/insect-history:/root/.local/share/insect-history sharkdp/insect:latest 534 | 535 | ## Maintainers 536 | 537 | - [sharkdp](https://github.com/sharkdp) 538 | - [mhmdanas](https://github.com/mhmdanas) 539 | -------------------------------------------------------------------------------- /benchmarks/computations-1.ins: -------------------------------------------------------------------------------- 1 | 1+1 2 | -------------------------------------------------------------------------------- /benchmarks/computations-160.ins: -------------------------------------------------------------------------------- 1 | 1920 / 16 * 9 2 | 40000 km / c -> ms 3 | list 4 | 2 min + 30 s 5 | sin(30 deg) 6 | 6 Mbit/s * 1.5 h -> GB 7 | r = 80 cm 8 | pi * r^2 -> m^2 9 | 1920 / 16 * 9 10 | 40000 km / c -> ms 11 | list 12 | 2 min + 30 s 13 | sin(30 deg) 14 | 6 Mbit/s * 1.5 h -> GB 15 | r = 80 cm 16 | pi * r^2 -> m^2 17 | 1920 / 16 * 9 18 | 40000 km / c -> ms 19 | list 20 | 2 min + 30 s 21 | sin(30 deg) 22 | 6 Mbit/s * 1.5 h -> GB 23 | r = 80 cm 24 | pi * r^2 -> m^2 25 | 1920 / 16 * 9 26 | 40000 km / c -> ms 27 | list 28 | 2 min + 30 s 29 | sin(30 deg) 30 | 6 Mbit/s * 1.5 h -> GB 31 | r = 80 cm 32 | pi * r^2 -> m^2 33 | 1920 / 16 * 9 34 | 40000 km / c -> ms 35 | list 36 | 2 min + 30 s 37 | sin(30 deg) 38 | 6 Mbit/s * 1.5 h -> GB 39 | r = 80 cm 40 | pi * r^2 -> m^2 41 | 1920 / 16 * 9 42 | 40000 km / c -> ms 43 | list 44 | 2 min + 30 s 45 | sin(30 deg) 46 | 6 Mbit/s * 1.5 h -> GB 47 | r = 80 cm 48 | pi * r^2 -> m^2 49 | 1920 / 16 * 9 50 | 40000 km / c -> ms 51 | list 52 | 2 min + 30 s 53 | sin(30 deg) 54 | 6 Mbit/s * 1.5 h -> GB 55 | r = 80 cm 56 | pi * r^2 -> m^2 57 | 1920 / 16 * 9 58 | 40000 km / c -> ms 59 | list 60 | 2 min + 30 s 61 | sin(30 deg) 62 | 6 Mbit/s * 1.5 h -> GB 63 | r = 80 cm 64 | pi * r^2 -> m^2 65 | 1920 / 16 * 9 66 | 40000 km / c -> ms 67 | list 68 | 2 min + 30 s 69 | sin(30 deg) 70 | 6 Mbit/s * 1.5 h -> GB 71 | r = 80 cm 72 | pi * r^2 -> m^2 73 | 1920 / 16 * 9 74 | 40000 km / c -> ms 75 | list 76 | 2 min + 30 s 77 | sin(30 deg) 78 | 6 Mbit/s * 1.5 h -> GB 79 | r = 80 cm 80 | pi * r^2 -> m^2 81 | 1920 / 16 * 9 82 | 40000 km / c -> ms 83 | list 84 | 2 min + 30 s 85 | sin(30 deg) 86 | 6 Mbit/s * 1.5 h -> GB 87 | r = 80 cm 88 | pi * r^2 -> m^2 89 | 1920 / 16 * 9 90 | 40000 km / c -> ms 91 | list 92 | 2 min + 30 s 93 | sin(30 deg) 94 | 6 Mbit/s * 1.5 h -> GB 95 | r = 80 cm 96 | pi * r^2 -> m^2 97 | 1920 / 16 * 9 98 | 40000 km / c -> ms 99 | list 100 | 2 min + 30 s 101 | sin(30 deg) 102 | 6 Mbit/s * 1.5 h -> GB 103 | r = 80 cm 104 | pi * r^2 -> m^2 105 | 1920 / 16 * 9 106 | 40000 km / c -> ms 107 | list 108 | 2 min + 30 s 109 | sin(30 deg) 110 | 6 Mbit/s * 1.5 h -> GB 111 | r = 80 cm 112 | pi * r^2 -> m^2 113 | 1920 / 16 * 9 114 | 40000 km / c -> ms 115 | list 116 | 2 min + 30 s 117 | sin(30 deg) 118 | 6 Mbit/s * 1.5 h -> GB 119 | r = 80 cm 120 | pi * r^2 -> m^2 121 | 1920 / 16 * 9 122 | 40000 km / c -> ms 123 | list 124 | 2 min + 30 s 125 | sin(30 deg) 126 | 6 Mbit/s * 1.5 h -> GB 127 | r = 80 cm 128 | pi * r^2 -> m^2 129 | 1920 / 16 * 9 130 | 40000 km / c -> ms 131 | list 132 | 2 min + 30 s 133 | sin(30 deg) 134 | 6 Mbit/s * 1.5 h -> GB 135 | r = 80 cm 136 | pi * r^2 -> m^2 137 | 1920 / 16 * 9 138 | 40000 km / c -> ms 139 | list 140 | 2 min + 30 s 141 | sin(30 deg) 142 | 6 Mbit/s * 1.5 h -> GB 143 | r = 80 cm 144 | pi * r^2 -> m^2 145 | 1920 / 16 * 9 146 | 40000 km / c -> ms 147 | list 148 | 2 min + 30 s 149 | sin(30 deg) 150 | 6 Mbit/s * 1.5 h -> GB 151 | r = 80 cm 152 | pi * r^2 -> m^2 153 | 1920 / 16 * 9 154 | 40000 km / c -> ms 155 | list 156 | 2 min + 30 s 157 | sin(30 deg) 158 | 6 Mbit/s * 1.5 h -> GB 159 | r = 80 cm 160 | pi * r^2 -> m^2 161 | -------------------------------------------------------------------------------- /benchmarks/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Get the directory containing this script, see 4 | # https://stackoverflow.com/a/29835459/704831 5 | benchmark_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) 6 | cd "$benchmark_dir" 7 | 8 | # Check that Hyperfine is installed. 9 | if ! command -v hyperfine > /dev/null 2>&1; then 10 | echo "'hyperfine' does not seem to be installed." 11 | echo "You can get it here: https://github.com/sharkdp/hyperfine" 12 | exit 1 13 | fi 14 | 15 | rm -rf results/ 16 | mkdir results/ 17 | 18 | hyperfine \ 19 | --warmup=5 \ 20 | --time-unit=millisecond \ 21 | --export-json results/startup.json \ 22 | --export-markdown results/startup.md \ 23 | --command-name "Startup time (insect '1+1')" \ 24 | "node ../index.cjs '1+1'" 25 | 26 | hyperfine \ 27 | --warmup=5 \ 28 | --time-unit=millisecond \ 29 | --export-json results/startup-stdin.json \ 30 | --export-markdown results/startup-stdin.md \ 31 | --command-name "Startup time, stdin mode (insect < computations-1.ins)" \ 32 | "node ../index.cjs < computations-1.ins" 33 | 34 | hyperfine \ 35 | --warmup=2 \ 36 | --time-unit=millisecond \ 37 | --export-json results/evaluation.json \ 38 | --export-markdown results/evaluation.md \ 39 | --command-name "Evaluation speed (insect < computations-160.ins)" \ 40 | "node ../index.cjs < computations-160.ins" 41 | 42 | for benchmark in startup startup-stdin evaluation; do 43 | cat "results/$benchmark.md" 44 | echo 45 | done > results/report.md 46 | cat results/report.md 47 | -------------------------------------------------------------------------------- /copy.js: -------------------------------------------------------------------------------- 1 | import { copyFileSync } from "fs"; 2 | 3 | copyFileSync("node_modules/keyboardevent-key-polyfill/index.js", "web/keyboardevent-key-polyfill.js"); 4 | copyFileSync("node_modules/jquery/dist/jquery.min.js", "web/jquery.min.js"); 5 | copyFileSync("node_modules/jquery.terminal/js/jquery.terminal.min.js", "web/jquery.terminal.min.js"); 6 | copyFileSync("node_modules/jquery.terminal/js/jquery.mousewheel-min.js", "web/jquery.mousewheel-min.js"); 7 | copyFileSync("node_modules/jquery.terminal/css/jquery.terminal.min.css", "web/terminal.css"); 8 | -------------------------------------------------------------------------------- /docs/development.md: -------------------------------------------------------------------------------- 1 | Development 2 | ----------- 3 | 4 | Insect is written in PureScript (see the [Getting Started](https://github.com/purescript/documentation/blob/master/guides/Getting-Started.md) guide). First, install all dependencies: 5 | 6 | npm install 7 | 8 | To start the web version: 9 | 10 | npm start 11 | 12 | To build a bundled JavaScript file that you can run from the terminal (note that 13 | this builds the web version too): 14 | 15 | npm run build 16 | 17 | To run the `index.cjs` file which the previous command creates: 18 | 19 | node index.cjs 20 | # Or simply on Un*x 21 | ./index.cjs 22 | 23 | Note that it's not possible to just move this file anywhere and then run it 24 | there, since it depends on packages in `node_modules`. 25 | 26 | Insect comes with a comprehensive set of [unit tests](test/Main.purs). To run 27 | them: 28 | 29 | npm test 30 | 31 | Note that Node.js 12 or above is required to work on/build Insect (despite 32 | Insect itself requiring only Node.js 10 or later to run). If you don't have or 33 | want to install Node.js 12 or later, you can use the following Dockerfile to 34 | build or run Insect on Node.js 18: 35 | 36 | ```Dockerfile 37 | FROM node:18 38 | 39 | WORKDIR /usr/src/insect 40 | 41 | COPY . . 42 | 43 | RUN npm install && \ 44 | npm run build 45 | 46 | CMD ["node", "index.cjs"] 47 | ``` 48 | 49 | After creating the image (`docker build -t sharkdp/insect .`), you can create 50 | the container and copy out the build artifacts: 51 | 52 | docker create sharkdp/insect:latest 53 | # copy SHA (e.g. 71f0797703e8) 54 | docker cp 71f0797703e8:/usr/src/insect/index.cjs . 55 | docker cp -r 71f0797703e8:/usr/src/insect/node_modules . 56 | 57 | 58 | To directly run Insect inside Docker (paying a heavy startup time penalty), you 59 | can use: 60 | 61 | docker run -it --rm -v ~/.local/share/insect-history:/root/.local/share/insect-history sharkdp/insect:latest 62 | 63 | Maintainers 64 | ----------- 65 | 66 | * [sharkdp](https://github.com/sharkdp) 67 | * [mhmdanas](https://github.com/mhmdanas) 68 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | FAQ 2 | --- 3 | 4 | - Why are Celsius and Fahrenheit not supported? 5 | 6 | In contrast to the SI unit of temperature, the [Kelvin](https://en.wikipedia.org/wiki/Kelvin), 7 | and to all other units, Celsius and Fahrenheit both require an additive offset when converting into 8 | and from other temperature units. This additive offset leads to all kinds of ambiguities when 9 | performing calculations in these units. Adding two temperatures in Celsius, for example, is 10 | only meaningful if one of them is seen as an offset value (rather than as an absolute 11 | temperature). Insect is primarily a scientific calculator (as opposed to a unit conversion 12 | tool) and therefore focuses on getting physical calculations right. 13 | 14 | Even though *°C* and *°F* are not supported as built-in units, there are helper functions to 15 | convert to and from Celsius (and Fahrenheit): 16 | 17 | - `fromCelsius` takes a **scalar value** that represents a temperature in Celsius and returns 18 | a corresponding **temperature in Kelvin**: 19 | 20 | ``` 21 | > fromCelsius(0) 22 | 23 | = 273.15 K 24 | 25 | > k_B * fromCelsius(23) to meV 26 | 27 | = 25.5202 meV 28 | ``` 29 | 30 | - `toCelsius` takes a **temperature in Kelvin** and returns a **scalar value** that 31 | represents the corresponding temperature in Celsius: 32 | 33 | ``` 34 | > toCelsius(70 K) 35 | 36 | = -203.15 37 | 38 | > toCelsius(25 meV / k_B) 39 | 40 | = 16.963 41 | ``` 42 | 43 | - Why is `1/2 x` parsed as `1/(2x)`? 44 | 45 | *Implicit* multiplication (without an explicit multiplication sign) has a higher precedence 46 | than division (see [operator precedence rules](#reference)). This is by design, in order to 47 | parse inputs like `50 cm / 2 m` as `(50 cm) / (2 m)`. If you meant *½ · x*, write `1/2 * x`. 48 | 49 | - What is the internal numerical precision? 50 | 51 | By default, Insect shows 6 significant digits in the result of the calculation. However, 52 | the internal numerical precision is much higher (30 digits). 53 | 54 | - How does the conversion operator work? 55 | 56 | The conversion operator `->` attempts to convert the physical quantity on its left hand side 57 | to the *unit of the expression* on its right hand side. This means that you can write an 58 | arbitrary expression on the right hand side (but only the unit part will be extracted). For 59 | example: 60 | 61 | ``` 62 | # simple unit conversion: 63 | > 120 km/h -> mph 64 | 65 | = 74.5645 mi/h 66 | 67 | # expression on the right hand side: 68 | > 120 m^3 -> km * m^2 69 | 70 | = 0.12 m²·km 71 | 72 | # convert x1 to the same unit as x2: 73 | > x1 = 50 km / h 74 | > x2 = 3 m/s -> x1 75 | 76 | x2 = 10.8 km/h 77 | ``` 78 | 79 | - What is the relation between the units `RPM`, `rad/s`, `deg/s` and `Hz`? 80 | 81 | The unit [`RPM`](https://en.wikipedia.org/wiki/Revolutions_per_minute) (revolutions per 82 | minute) is defined via `1 RPM = 1 / minute` where the `1` on the right hand side symbolizes 83 | "1 revolution". 84 | 85 | As the base unit is the same (`1 / second`), `RPM` can be converted to `rad / s`, `deg / s` or 86 | `Hz`. Note, however, that `1 RPM` does *not* equal `2π rad / min` or `360° / min` or `1 Hz`, as 87 | some might expect. If you're interested in computing the traversed angle of something that 88 | rotates with a given number of revolutions per minute, you need to multiply by `2π rad` or 89 | `360°` because: 90 | ``` 91 | 1 RPM · (360°/revolution) = (1 revolution / minute) · (360° / revolution) = 360° / minute 92 | ``` 93 | -------------------------------------------------------------------------------- /docs/features.md: -------------------------------------------------------------------------------- 1 | Documentation 2 | ------------- 3 | 4 | - **Evaluate mathematical expressions**: 5 | 6 | ``` 7 | 1920/16*9 8 | 2^32 9 | sqrt(1.4^2 + 1.5^2) * cos(pi/3)^2 10 | ``` 11 | 12 | * **Operators**: addition (`+`), subtraction (`-`), 13 | multiplication (`*`, `·`, `×`), division (`/`, `÷`, `per`), 14 | exponentiation (`^`, `**`). Full list: see [*Reference*](#reference) below. 15 | 16 | * **Mathematical functions**: `abs`, `acos`, `acosh`, `acot`/`arccotangent`, 17 | `acoth`/`archypcotangent`, `acsc`/`arccosecant`, `acsch`/`archypcosecant`, `arcsecant`, 18 | `asech`/`archypsecant`, `asin`, `asinh`, `atan2`, `atan`, `atanh`, `ceil`, `cos`, `cosh`, 19 | `cot`/`cotangent`, `coth`/`hypcotangent`, `csc`/`cosecant`, `csch`/`hypcosecant`, `exp`, 20 | `floor`, `fromCelsius`, `fromFahrenheit`, `gamma`, `ln`, `log10`, `log`, `maximum`, `mean`, 21 | `minimum`, `round`, `secant`, `sech`/`hypsecant`, `sin`, `sinh`, `sqrt`, `tan`, `tanh`, 22 | `toCelsius`, `toFahrenheit`. 23 | 24 | * **High-precision numeric type** with *30* significant digits that can handle 25 | *very* large (or small) exponents like *10^(10^10)*. 26 | 27 | * **Exponential notation**: `6.022e23`. 28 | 29 | * **Hexadecimal, octal and binary number input**: 30 | 31 | ``` 32 | 0xFFFF 33 | 0b1011 34 | 0o32 35 | 0x2.F 36 | 0o5p3 37 | ``` 38 | 39 | - **Physical units**: parsing and handling, including metric prefixes: 40 | 41 | ``` 42 | 2 min + 30 s 43 | 40 kg * 9.8 m/s^2 * 150 cm 44 | sin(30°) 45 | ``` 46 | 47 | * **Supported units**: see [*Reference*](#reference) section below. 48 | 49 | * **Implicit conversions**: `15 km/h * 30 min` evaluates to `7.5 km`. 50 | 51 | * **Useful error messages**: 52 | 53 | ``` 54 | > 2 watts + 4 newton meter 55 | 56 | Conversion error: 57 | Cannot convert unit N·m (base units: kg·m²·s⁻²) 58 | to unit W (base units: kg·m²·s⁻³) 59 | ``` 60 | 61 | - **Explicit unit conversions**: the `->` conversion operator (aliases: `→`, `➞`, `to`): 62 | 63 | ``` 64 | 60 mph -> m/s 65 | 500 km/day -> km/h 66 | 1 mrad -> degree 67 | 52 weeks -> days 68 | 5 in + 2 ft -> cm 69 | atan(30 cm / 2 m) -> degree 70 | 6 Mbit/s * 1.5 h -> GB 71 | ``` 72 | 73 | - **Variable assignments**: 74 | 75 | Example: mass of the earth 76 | ``` 77 | r = 6000km 78 | vol = 4/3 * pi * r^3 79 | density = 5 g/cm^3 80 | vol * density -> kg 81 | ``` 82 | 83 | Example: oscillation period of a pendulum 84 | ``` 85 | len = 20 cm 86 | 2pi*sqrt(len/g0) -> ms 87 | ``` 88 | 89 | * **Predefined constants** (type `list` to see them all): speed of light (`c`), 90 | Planck's constant (`h_bar`), electron mass (`electronMass`), elementary charge 91 | (`elementaryCharge`), magnetic constant (`µ0`), electric constant (`eps0`), 92 | Bohr magneton (`µ_B`), Avogadro's constant (`N_A`), Boltzmann constant 93 | (`k_B`), gravitational acceleration (`g0`), ideal gas constant (`R`), ... 94 | 95 | * **Last result**: you can use `ans` (answer) or `_` to refer to the result of the 96 | last calculation. 97 | 98 | - **User-defined functions**: 99 | 100 | Example: kinetic energy 101 | ``` 102 | kineticEnergy(mass, speed) = 0.5 * mass * speed^2 -> kJ 103 | 104 | kineticEnergy(800 kg, 120 km/h) 105 | ``` 106 | 107 | Example: barometric formula 108 | ``` 109 | P0 = 1 atm 110 | T0 = fromCelsius(15) 111 | tempGradient = 0.65 K / 100 m 112 | 113 | pressure(height) = P0 * (1 - tempGradient * height / T0)^5.255 -> hPa 114 | 115 | pressure(1500 m) 116 | ``` 117 | 118 | - **Sums and products**: 119 | 120 | Syntax: 121 | ``` 122 | sum(<expression>, <index-variable>, <from>, <to>) 123 | product(<expression>, <index-variable>, <from>, <to>) 124 | ``` 125 | 126 | Examples: 127 | ``` 128 | # sum of the first ten squares 129 | sum(k^2, k, 1, 10) 130 | 131 | # the factorial of n as the product 1 × 2 × ... × n 132 | myFactorial(n) = product(k, k, 1, n) 133 | ``` 134 | 135 | - **Unicode support**: 136 | 137 | ``` 138 | λ = 2 × 300 µm 139 | ν = c/λ → GHz 140 | ``` 141 | 142 | - **And more**: tab completion, command history (arrow keys, `Ctrl`+`R`), pretty printing, syntax 143 | highlighting, ... 144 | -------------------------------------------------------------------------------- /docs/generate: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Get the directory containing this script, see 4 | # https://stackoverflow.com/a/29835459/704831 5 | docs_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) 6 | cd "$docs_dir" 7 | 8 | input_format=gfm-hard_line_breaks 9 | 10 | # Generate combined markdown document for README.md 11 | pandoc --from="$input_format" \ 12 | --to=gfm-raw_html \ 13 | --output=../README.md \ 14 | --standalone \ 15 | --template=template.readme \ 16 | --toc \ 17 | features.md \ 18 | reference-syntax.md \ 19 | reference-units.md \ 20 | pros-and-cons.md \ 21 | faq.md \ 22 | terminal-version.md \ 23 | development.md & 24 | 25 | # Generate manpage 26 | pandoc --from="$input_format" \ 27 | --to=man \ 28 | --output=insect.1 \ 29 | --standalone \ 30 | --reference-links \ 31 | -V section=1 \ 32 | -V header="insect - scientific calculator" \ 33 | manpage-header.md \ 34 | features.md \ 35 | reference-syntax.md \ 36 | faq.md \ 37 | manpage-footer.md & 38 | 39 | wait 40 | -------------------------------------------------------------------------------- /docs/manpage-footer.md: -------------------------------------------------------------------------------- 1 | # AUTHOR 2 | 3 | Written by David Peter <mail@david-peter.de>. 4 | 5 | # REPORTING BUGS 6 | 7 | Please report bugs on GitHub: <https://github.com/sharkdp/insect>. 8 | 9 | # COPYRIGHT 10 | 11 | insect is MIT-licensed. For details, see <https://github.com/sharkdp/insect>. 12 | 13 | # SEE ALSO 14 | 15 | - Full documentation at <https://github.com/sharkdp/insect>. 16 | - Web version at <https://insect.sh>. 17 | -------------------------------------------------------------------------------- /docs/manpage-header.md: -------------------------------------------------------------------------------- 1 | # NAME 2 | 3 | insect - **in**teractive **sc**i**e**ntific **c**alcula**t**or 4 | 5 | # SYNOPSIS 6 | 7 | `insect [EXPR]` 8 | 9 | # DESCRIPTION 10 | 11 | *insect* is a high-precision scientific calculator with full support for physical units. 12 | 13 | It can be used in interactive mode by simply calling *insect*. This mode can also be used to read 14 | from a file, by simply piping the file contents via *insect < my-calculation.ins*. 15 | If you want to evaluate just a single expression, pass it as an argument: *insect '70mph to km/h'*. 16 | -------------------------------------------------------------------------------- /docs/pros-and-cons.md: -------------------------------------------------------------------------------- 1 | Pros and cons 2 | ------------- 3 | 4 | **Reasons to use Insect** 5 | 6 | - Insect is **open-source**. 7 | - There is a [web version](https://Insect.sh/) that requires **no installation**. 8 | - With both browser and terminal versions available, Insect is truly **cross-platform**. 9 | - Insect has *first-class* support for **physical units**, including metric and binary prefixes. 10 | While evaluating your calculation, Insect ensures that you did not accidentally make any 11 | mistakes in combining the physical quantities. 12 | - Insect supports an [**interactive**](https://en.wikipedia.org/wiki/REPL) style with its 13 | readline-like interface. There is a saved history that can be browsed by pressing the up and 14 | down arrow keys. The history is also searchable via *Ctrl-R*. 15 | - Insect's syntax is rather strict. The parser does not try to be "smart" on syntactically 16 | incorrect input, so there shouldn't be any surprises - and you can trust the result of your 17 | calculation. The parsed user input is always pretty-printed for a quick double-check. 18 | - Insect is written in [PureScript](http://www.purescript.org/) and therefore benefits from 19 | all the safety guarantees that a strictly typed functional programming language gives you. 20 | - The source code of [purescript-quantities](https://github.com/sharkdp/purescript-quantities) 21 | (the underlying library for physical units) as well as the code of Insect itself is 22 | **extensively tested**. 23 | 24 | **Reasons to choose an alternative** 25 | 26 | - Insect is a scientific calculator. It's not a computer algebra system that solves differential 27 | equations or computes integrals. Try *[WolframAlpha](http://www.wolframalpha.com/)* instead. 28 | - There is no graphical user interface with buttons for each action (*x²*, *1/x*, *DEG/RAD*, 29 | etc.). *[Qalculate!](http://qalculate.github.io/)* is a fantastic tool that supports both 30 | text as well as graphical input. 31 | - Insect supports a huge range of physical units: all 32 | [SI units](https://en.wikipedia.org/wiki/International_System_of_Units), all non-SI units that are 33 | accepted by SI as well as most units of the imperial and US customary systems (and many more). 34 | However, if you need something even more comprehensive, try 35 | *[GNU units](https://www.gnu.org/software/units/)*. 36 | - Insect is not a general-purpose programming language. You could try 37 | *[Frink](https://frinklang.org/)*. 38 | - Insect does not have a special mode for hexadecimal, octal, or binary numbers (yet), though it 39 | does support inputting them. 40 | -------------------------------------------------------------------------------- /docs/reference-syntax.md: -------------------------------------------------------------------------------- 1 | Reference 2 | --------- 3 | 4 | - Operators (ordered by precedence: high to low) 5 | 6 | | Operator | Syntax | 7 | | ------------------------- | -------------------- | 8 | | factorial | `!` | 9 | | square, cube, ... | `²`, `³`, `⁻¹`, ... | 10 | | exponentiation | `^`, `**` | 11 | | multiplication (implicit) | *whitespace* | 12 | | modulo | `%` | 13 | | division | `per` | 14 | | division | `/`, `÷` | 15 | | multiplication (explicit) | `*`, `·`, `×` | 16 | | subtraction | `-` | 17 | | addition | `+` | 18 | | unit conversion | `->`, `→`, `➞`, `to` | 19 | | assignment | `=` | 20 | 21 | Note that *implicit* multiplication has a higher precedence than division, i.e. `50 cm / 2 m` will be parsed as `50 cm / (2 m)`. 22 | 23 | - Commands 24 | 25 | | Command | Syntax | 26 | | ------------------------ | ------------------ | 27 | | help text | `help`, `?` | 28 | | list of variables | `list`, `ls`, `ll` | 29 | | reset environment | `reset` | 30 | | clear screen | `clear`, `cls` | 31 | | copy result to clipboard | `copy`, `cp` | 32 | | quit (CLI) | `quit`, `exit` | 33 | -------------------------------------------------------------------------------- /docs/reference-units.md: -------------------------------------------------------------------------------- 1 | - Supported units (remember that you can use tab completion). 2 | 3 | All SI-accepted units support metric prefixes and [binary 4 | prefixes](https://en.wikipedia.org/wiki/Binary_prefix) (`MiB`, `GiB`, ...). 5 | 6 | | Unit | Syntax | 7 | | ---- | ------ | 8 | | [Ampere](https://en.wikipedia.org/wiki/Ampere) | `amperes`, `ampere`, `A` | 9 | | [Ångström](https://en.wikipedia.org/wiki/Ångström) | `angstroms`, `angstrom`, `Å` | 10 | | [Astronomical unit](https://en.wikipedia.org/wiki/Astronomical_unit) | `AU`, `au`, `astronomicalunits`, `astronomicalunit` | 11 | | [Atmosphere](https://en.wikipedia.org/wiki/Atmosphere_(unit)) | `atm` | 12 | | [Bar](https://en.wikipedia.org/wiki/Bar_(unit)) | `bars`, `bar` | 13 | | [Barn](https://en.wikipedia.org/wiki/Barn_(unit)) | `barns`, `barn` | 14 | | [Becquerel](https://en.wikipedia.org/wiki/Becquerel) | `becquerels`, `becquerel`, `Bq` | 15 | | [Bel](https://en.wikipedia.org/wiki/Decibel) | `bels`, `bel` | 16 | | [Bit](https://en.wikipedia.org/wiki/Bit) | `bits`, `bit` | 17 | | [Bits per second](https://en.wikipedia.org/wiki/Data_rate_units) | `bps` | 18 | | [British thermal unit](https://en.wikipedia.org/wiki/British_thermal_unit) | `BTU` | 19 | | [Byte](https://en.wikipedia.org/wiki/Byte) | `Bytes`, `bytes`, `Byte`, `byte`, `B`, `Octets`, `octets`, `Octet`, `octet`| 20 | | [Calorie](https://en.wikipedia.org/wiki/Calorie) | `calories`, `calorie`, `cal` | 21 | | [Candela](https://en.wikipedia.org/wiki/Candela) | `candelas`, `candela`, `cd` | 22 | | [Coulomb](https://en.wikipedia.org/wiki/Coulomb) | `coulombs`, `coulomb`, `C` | 23 | | [Cup](https://en.wikipedia.org/wiki/Cup_(unit)) | `cups`, `cup` | 24 | | [DPI](https://en.wikipedia.org/wiki/Dots_per_inch) | `dpi` | 25 | | [Day](https://en.wikipedia.org/wiki/Day) | `days`, `day`, `d` | 26 | | [Degree](https://en.wikipedia.org/wiki/Degree_(angle)) | `degrees`, `degree`, `deg`, `°` | 27 | | [Dot](https://en.wikipedia.org/wiki/Dots_per_inch) | `dots`, `dot` | 28 | | [Electronvolt](https://en.wikipedia.org/wiki/Electronvolt) | `electronvolts`, `electronvolt`, `eV` | 29 | | [Euro](https://en.wikipedia.org/wiki/Euro) | `euros`, `euro`, `EUR`, `€` | 30 | | [Farad](https://en.wikipedia.org/wiki/Farad) | `farads`, `farad`, `F` | 31 | | [Fluid ounce](https://en.wikipedia.org/wiki/Fluid_ounce) | `fluidounces`, `fluidounce`, `floz` | 32 | | [Foot](https://en.wikipedia.org/wiki/Foot_(unit)) | `feet`, `foot`, `ft` | 33 | | [Fortnight](https://en.wikipedia.org/wiki/Fortnight) | `fortnights`, `fortnight` | 34 | | [Frame](https://en.wikipedia.org/wiki/Film_frame) | `frames`, `frame` | 35 | | [Frames per second](https://en.wikipedia.org/wiki/Frame_rate) | `fps` | 36 | | [Furlong](https://en.wikipedia.org/wiki/Furlong) | `furlongs`, `furlong` | 37 | | [Gallon](https://en.wikipedia.org/wiki/Gallon) | `gallons`, `gallon`, `gal` | 38 | | [Gauss](https://en.wikipedia.org/wiki/Gauss_(unit)) | `gauss` | 39 | | [Gram](https://en.wikipedia.org/wiki/Gram) | `grams`, `gram`, `grammes`, `gramme`, `g` | 40 | | [Gray](https://en.wikipedia.org/wiki/Gray_(unit)) | `grays`, `gray`, `Gy` | 41 | | [Hectare](https://en.wikipedia.org/wiki/Hectare) | `hectares`, `hectare`, `ha` | 42 | | [Henry](https://en.wikipedia.org/wiki/Henry_(unit)) | `henrys`, `henries`, `henry`, `H` | 43 | | [Hertz](https://en.wikipedia.org/wiki/Hertz) | `hertz`, `Hz` | 44 | | [Hogshead](https://en.wikipedia.org/wiki/Hogshead) | `hogsheads`, `hogshead` | 45 | | [Hour](https://en.wikipedia.org/wiki/Hour) | `hours`, `hour`, `hr`, `h` | 46 | | [Inch](https://en.wikipedia.org/wiki/Inch) | `inches`, `inch`, `in` | 47 | | [Joule](https://en.wikipedia.org/wiki/Joule) | `joules`, `joule`, `J` | 48 | | [Katal](https://en.wikipedia.org/wiki/Katal) | `katals`, `katal`, `kat` | 49 | | [Knot](https://en.wikipedia.org/wiki/Knot_(unit)) | `knots`, `knot`, `kn`, `kt` | 50 | | [Kelvin](https://en.wikipedia.org/wiki/Kelvin) | `kelvins`, `kelvin`, `K` | 51 | | [Light-year](https://en.wikipedia.org/wiki/Light-year) | `lightyears`, `lightyear`, `ly` | 52 | | [Liter](https://en.wikipedia.org/wiki/Liter) | `liters`, `liter`, `litres`, `litre`, `L`, `l` | 53 | | [Lumen](https://en.wikipedia.org/wiki/Lumen_(unit)) | `lumens`, `lumen`, `lm` | 54 | | [Lux](https://en.wikipedia.org/wiki/Lux) | `lux`, `lx` | 55 | | [Meter](https://en.wikipedia.org/wiki/Meter) | `meters`, `meter`, `metres`, `metre`, `m` | 56 | | [Mile](https://en.wikipedia.org/wiki/Mile) | `miles`, `mile` | 57 | | [Miles per hour](https://en.wikipedia.org/wiki/Miles_per_hour) | `mph` | 58 | | [Millimeter of mercury](https://en.wikipedia.org/wiki/Millimeter_of_mercury) | `mmHg` | 59 | | [Minute](https://en.wikipedia.org/wiki/Minute) | `minutes`, `minute`, `min` | 60 | | [Molal](https://en.wikipedia.org/wiki/Molality#Unit) | `molals`, `molal` | 61 | | [Molar](https://en.wikipedia.org/wiki/Molar_concentration#Units) | `molars`, `molar` | 62 | | [Mole](https://en.wikipedia.org/wiki/Mole_(unit)) | `moles`, `mole`, `mol` | 63 | | [Month](https://en.wikipedia.org/wiki/Month) | `months`, `month` | 64 | | [Nautical mile](https://en.wikipedia.org/wiki/Nautical_mile) | `M`, `NM`, `nmi` | 65 | | [Newton](https://en.wikipedia.org/wiki/Newton_(unit)) | `newtons`, `newton`, `N` | 66 | | [Ohm](https://en.wikipedia.org/wiki/Ohm) | `ohms`, `ohm`, `Ω` | 67 | | [Ounce](https://en.wikipedia.org/wiki/Ounce) | `ounces`, `ounce`, `oz` | 68 | | [PPI](https://en.wikipedia.org/wiki/Pixels_per_inch) | `ppi` | 69 | | [Parsec](https://en.wikipedia.org/wiki/Parsec) | `parsecs`, `parsec`, `pc` | 70 | | [Parts-per-million](https://en.wikipedia.org/wiki/Parts-per_notation) | `ppm` | 71 | | [Parts-per-billion](https://en.wikipedia.org/wiki/Parts-per_notation) | `ppb` | 72 | | [Parts-per-trillion](https://en.wikipedia.org/wiki/Parts-per_notation) | `ppt` | 73 | | [Parts-per-quadrillion](https://en.wikipedia.org/wiki/Parts-per_notation) | `ppq` | 74 | | [Pascal](https://en.wikipedia.org/wiki/Pascal_(unit)) | `pascals`, `pascal`, `Pa` | 75 | | [Percent](https://en.wikipedia.org/wiki/Parts-per_notation) | `percent`, `pct` | 76 | | [Person](https://en.wiktionary.org/wiki/person) | `persons`, `person`, `people` | 77 | | [Piece](https://en.wiktionary.org/wiki/piece) | `pieces`, `piece` | 78 | | [Pint](https://en.wikipedia.org/wiki/Pint) | `pints`, `pint` | 79 | | [Pixel](https://en.wikipedia.org/wiki/Pixel) | `pixels`, `pixel`, `px` | 80 | | [Pound-force](https://en.wikipedia.org/wiki/Pound_%28force%29) | `pound_force`, `lbf` | 81 | | [Pound](https://en.wikipedia.org/wiki/Pound_(mass)) | `pounds`, `pound`, `lb` | 82 | | [Psi](https://en.wikipedia.org/wiki/Pounds_per_square_inch) | `psi` | 83 | | [RPM](https://en.wikipedia.org/wiki/RPM) | `RPM`, `rpm` | 84 | | [Radian](https://en.wikipedia.org/wiki/Radian) | `radians`, `radian`, `rad` | 85 | | [Rod](https://en.wikipedia.org/wiki/Rod_(unit)) | `rods`, `rod` | 86 | | [Second](https://en.wikipedia.org/wiki/Second) | `seconds`, `second`, `sec`, `s` | 87 | | [Siemens](https://en.wikipedia.org/wiki/Siemens_(unit)) | `siemens`, `S` | 88 | | [Sievert](https://en.wikipedia.org/wiki/Sievert) | `sieverts`, `sievert`, `Sv` | 89 | | [Tablespoon](https://en.wikipedia.org/wiki/Tablespoon) | `tablespoons`, `tablespoon`, `tbsp` | 90 | | [Teaspoon](https://en.wikipedia.org/wiki/Teaspoon) | `teaspoons`, `teaspoon`, `tsp` | 91 | | [Tesla](https://en.wikipedia.org/wiki/Tesla_(unit)) | `teslas`, `tesla`, `T` | 92 | | [Thou](https://en.wikipedia.org/wiki/Thousandth_of_an_inch) | `thou`, `mils`, `mil` | 93 | | [Tonne](https://en.wikipedia.org/wiki/Tonne) | `tonnes`, `tonne`, `tons`, `ton`, `t` | 94 | | [US Dollar](https://en.wikipedia.org/wiki/USD) | `dollars`, `dollar`, `USD`, `
The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
| 95 | | [Volt](https://en.wikipedia.org/wiki/Volt) | `volts`, `volt`, `V` | 96 | | [Watt-hour](https://en.wikipedia.org/wiki/Kilowatt_hour) | `Wh` | 97 | | [Watt](https://en.wikipedia.org/wiki/Watt) | `watts`, `watt`, `W` | 98 | | [Weber](https://en.wikipedia.org/wiki/Weber_(unit)) | `webers`, `weber`, `Wb` | 99 | | [Week](https://en.wikipedia.org/wiki/Week) | `weeks`, `week` | 100 | | [Yard](https://en.wikipedia.org/wiki/Yard) | `yards`, `yard`, `yd` | 101 | | [Gregorian year](https://en.wikipedia.org/wiki/Gregorian_year) | `years`, `year` | 102 | | [Julian year](https://en.wikipedia.org/wiki/Julian_year_(astronomy)) | `julianYears`, `julianYear` | 103 | -------------------------------------------------------------------------------- /docs/template.readme: -------------------------------------------------------------------------------- 1 | <!-- 2 | 3 | --- IMPORTANT --- IMPORTANT --- IMPORTANT --- 4 | 5 | Do not edit this README file directly. Edit 6 | the files in the `docs` folder instead. 7 | 8 | --- IMPORTANT --- IMPORTANT --- IMPORTANT --- 9 | 10 | --> 11 | 12 | ![insect](web/media/insect.png "insect - scientific calculator") 13 | 14 | A high-precision scientific calculator with full support for physical units. 15 | 16 | **Try the web version here**: https://insect.sh 17 | 18 | Contents 19 | -------- 20 | 21 | $if(toc)$ 22 | $table-of-contents$ 23 | 24 | $endif$ 25 | $body$ 26 | $for(include-after)$ 27 | 28 | $include-after$ 29 | $endfor$ 30 | -------------------------------------------------------------------------------- /docs/terminal-version.md: -------------------------------------------------------------------------------- 1 | Terminal version 2 | ---------------- 3 | 4 | In addition to the web interface, there is also a command-line version 5 | (supporting Node.js 10 and later) which can by installed via 6 | [npm](https://www.npmjs.com/package/insect): 7 | 8 | npm install -g insect 9 | 10 | Note that you should almost always never run this as root or with `sudo`. If the 11 | command fails due to permission issues, [set up a prefix 12 | directory](https://github.com/sindresorhus/guides/blob/master/npm-global-without-sudo.md#install-npm-packages-globally-without-sudo-on-macos-and-linux) 13 | and call `npm install` as a user instead. 14 | 15 | For Arch Linux, there is an [AUR 16 | package](https://aur.archlinux.org/packages/insect/): 17 | 18 | yaourt -S insect 19 | 20 | For macOS, there is a [Homebrew formula](https://formulae.brew.sh/formula/insect): 21 | 22 | brew install insect 23 | 24 | For Android, install [Termux](https://termux.com/) from [F-Droid](https://f-droid.org/packages/com.termux/). Install Node.js in Termux and then install `insect` from npm: 25 | 26 | pkg install nodejs-lts 27 | npm install -g insect 28 | -------------------------------------------------------------------------------- /generate-third-party-licenses.cjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Generates `LICENSE_THIRDPARTY` (for the Insect CLI) and 4 | // `web/third-party-licenses.txt` (for the Insect website) files that include 5 | // the licenses of third-party software used by Insect. 6 | 7 | const exec = require("util").promisify(require("child_process").exec); 8 | const fs = require("fs"); 9 | const { join: pathJoin } = require("path"); 10 | 11 | function processDeps(deps) { 12 | let thirdPartyLicenses = ""; 13 | 14 | for (const dep of deps) { 15 | thirdPartyLicenses += `-------------------------------------------------------------------------------------\n${dep.name} ${dep.version}\n\n`; 16 | 17 | if (dep.name === "@jcubic/lily") { 18 | // This package has no license file, so use notice from 19 | // https://github.com/jcubic/lily/blob/6f40c5b01af540248e546be183f97338be864097/index.js#L4-L7 20 | // for now. 21 | // 22 | // Also see https://github.com/jcubic/lily/issues/8. 23 | thirdPartyLicenses += 24 | "Copyright (C) 2020-2021 Jakub T. Jankiewicz\n\nReleased under MIT license\n\n"; 25 | continue; 26 | } 27 | 28 | const licenseFiles = fs 29 | .readdirSync(dep.path) 30 | .filter((file) => /^(licen[sc]e|copying|notice)/i.test(file)) 31 | .map((file) => pathJoin(dep.path, file)); 32 | 33 | if (licenseFiles.length === 0) { 34 | console.error( 35 | `Could not find any license files for ${dep.name} ${dep.version}` 36 | ); 37 | process.exit(1); 38 | } 39 | 40 | for (const licenseFile of licenseFiles) { 41 | thirdPartyLicenses += fs 42 | .readFileSync(licenseFile, { encoding: "utf8" }) 43 | .replaceAll("\r\n", "\n") // Some license files use DOS newlines. 44 | .trim(); 45 | thirdPartyLicenses += "\n\n"; 46 | } 47 | } 48 | 49 | return thirdPartyLicenses.trim(); 50 | } 51 | 52 | (async () => { 53 | const [npmDeps, spagoDeps] = await Promise.all([ 54 | exec("npm ls --json --long --omit dev --all").then(({ stdout }) => { 55 | function go(json) { 56 | const deps = []; 57 | 58 | for (const depName in json) { 59 | const dep = json[depName]; 60 | 61 | // `fsevents` has an undefined path and version for whatever reason. 62 | if (dep.path === undefined) { 63 | continue; 64 | } 65 | 66 | deps.push({ name: depName, path: dep.path, version: dep.version }); 67 | 68 | if (dep.dependencies) { 69 | deps.push(...go(dep.dependencies)); 70 | } 71 | } 72 | 73 | return deps; 74 | } 75 | 76 | return go(JSON.parse(stdout).dependencies); 77 | }), 78 | exec("npx spago ls deps --transitive --json").then(({ stdout }) => 79 | stdout 80 | .trim() 81 | .split("\n") 82 | .map((line) => { 83 | const { packageName, version } = JSON.parse(line); 84 | return { 85 | name: `purescript-${packageName}`, 86 | // Remove `v` version prefix, if any. 87 | version: version.replace(/^v/, ""), 88 | path: pathJoin(".spago", packageName, version), 89 | }; 90 | }) 91 | ), 92 | ]); 93 | 94 | const npmDepsLicenses = processDeps(npmDeps); 95 | const spagoDepsLicenses = processDeps(spagoDeps); 96 | 97 | fs.writeFileSync("LICENSE_THIRDPARTY", spagoDepsLicenses); 98 | fs.writeFileSync( 99 | "web/third-party-licenses.txt", 100 | `${npmDepsLicenses}\n\n${spagoDepsLicenses}` 101 | ); 102 | })(); 103 | -------------------------------------------------------------------------------- /index.dev.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as Insect from "./output/Insect/index.js"; 4 | import * as path from "path"; 5 | import * as xdgBasedir from "xdg-basedir"; 6 | 7 | var insectEnv = Insect.initialEnvironment; 8 | 9 | function usage() { 10 | console.log("Usage: insect [EXPR]"); 11 | process.exit(1); 12 | } 13 | 14 | function runInsect(fmt, line) { 15 | var lineTrimmed = line.trim(); 16 | if (lineTrimmed === "" || lineTrimmed[0] === "#") { 17 | return undefined; 18 | } 19 | 20 | // Run insect 21 | var res = Insect.repl(fmt)(insectEnv)(line); 22 | 23 | // Update environment 24 | insectEnv = res.newEnv; 25 | 26 | return res; 27 | } 28 | 29 | // Top-level await is not supported in Node 12 and earlier. 30 | (async function() { 31 | if (process.env.INSECT_NO_RC !== "true") { 32 | var [util, lineReader, os] = await Promise.all([import("util"), import("line-reader"), import("os")]); 33 | 34 | var rcFilePaths = [path.join(xdgBasedir.config, "insect/insectrc"), path.join(os.homedir(), ".insectrc")]; 35 | var eachLine = util.promisify(lineReader.eachLine); 36 | 37 | for (const rcFilePath of rcFilePaths) { 38 | try { 39 | await eachLine(rcFilePath, function(line) { 40 | var res = runInsect(Insect.fmtPlain, line); 41 | // We really only care when it breaks 42 | if (res && res.msgType === "error") { 43 | console.error(res.msg); 44 | process.exit(1); 45 | } 46 | }); 47 | break; 48 | } catch (err) { 49 | if (err.code !== "ENOENT") { 50 | throw err; 51 | } 52 | } 53 | } 54 | } 55 | 56 | // Handle command line arguments 57 | var args = process.argv.slice(2); 58 | if (args[0] === "-h" || args[0] === "--help") { 59 | usage(); 60 | } else if (args.length !== 0) { 61 | // Execute a single command 62 | var res = runInsect(Insect.fmtPlain, args.join(" ")); 63 | if (res.msgType === "value" || res.msgType === "info") { 64 | console.log(res.msg); 65 | } else if (res.msgType === "error") { 66 | console.error(res.msg); 67 | process.exit(1); 68 | } 69 | process.exit(0); 70 | } 71 | 72 | var interactive = process.stdin.isTTY; 73 | 74 | if (interactive) { 75 | var [fs, clipboardy, readline] = await Promise.all([import("fs"), import("clipboardy"), import("readline")]); 76 | 77 | // Create `xdgBasedir.data` if it doesn't already exist. 78 | // See https://github.com/sharkdp/insect/issues/364. 79 | try { 80 | fs.mkdirSync(xdgBasedir.data, { recursive: true }); 81 | } catch (err) { 82 | if (err.code !== "EEXIST") { 83 | throw err; 84 | } 85 | } 86 | 87 | // Open the history file for reading and appending. 88 | var historyFd = fs.openSync(path.join(xdgBasedir.data, "insect-history"), 'a+'); 89 | 90 | var maxHistoryLength = 5000; 91 | 92 | // Set up REPL 93 | var rl = readline.createInterface({ 94 | input: process.stdin, 95 | output: process.stdout, 96 | history: fs.readFileSync(historyFd, "utf8").split("\n").slice(0, -1).reverse().slice(0, maxHistoryLength), 97 | historySize: maxHistoryLength, 98 | completer: function(line) { 99 | var identifiers = Insect.identifiers(insectEnv); 100 | 101 | var keywords = 102 | identifiers.concat(Insect.functions(insectEnv), Insect.supportedUnits, Insect.commands); 103 | 104 | var lastWord = line; 105 | if (line.trim() !== "") { 106 | var words = line.split(/\b/); 107 | lastWord = words[words.length - 1]; 108 | keywords = keywords.filter(function(kw) { 109 | return kw.indexOf(lastWord) === 0; 110 | }); 111 | } 112 | 113 | return [keywords, lastWord]; 114 | }, 115 | }); 116 | 117 | var prompt = '\x1b[01m>>>\x1b[0m '; 118 | 119 | // The visual length of the prompt (4) needs to be set explicitly for 120 | // older versions of node: 121 | rl.setPrompt(prompt, 4); 122 | 123 | rl.on('line', function(line) { 124 | var res = runInsect(Insect.fmtConsole, line); 125 | 126 | if (res) { 127 | if (res.msgType === "quit") { 128 | process.exit(0); 129 | } else if (res.msgType === "clear") { 130 | process.stdout.write('\x1Bc'); 131 | } else if (res.msgType === "copy") { 132 | if (res.msg === "") { 133 | console.log("\nNo result to copy.\n"); 134 | } else { 135 | clipboardy.writeSync(res.msg); 136 | console.log("\nCopied result '" + res.msg + "' to clipboard.\n"); 137 | } 138 | } else { 139 | console.log(res.msg + "\n"); 140 | } 141 | } 142 | 143 | rl.prompt(); 144 | }).on('SIGINT', function() { 145 | rl.clearLine(); 146 | rl.prompt(); 147 | }).on('close', function() { 148 | process.exit(0); 149 | }); 150 | 151 | // `createWriteStream` doesn't care about the first argument if it's given 152 | // `fd`. 153 | var historyStream = fs.createWriteStream(undefined, {fd: historyFd}); 154 | 155 | var oldAddHistory = rl._addHistory; 156 | 157 | // TODO: figure out how to do this without resorting to Node.js internals. 158 | rl._addHistory = function() { 159 | var last = rl.history[0]; 160 | 161 | var line = oldAddHistory.call(rl); 162 | 163 | if (line && line !== last) { 164 | historyStream.write(line + "\n"); 165 | } 166 | 167 | return line; 168 | }; 169 | 170 | rl.prompt(); 171 | } else { 172 | if (typeof lineReader === "undefined") { 173 | var lineReader = await import("line-reader"); 174 | } 175 | 176 | // Read from non-interactive stream (shell pipe) 177 | lineReader.eachLine(process.stdin, function(line) { 178 | var res = runInsect(Insect.fmtPlain, line); 179 | if (res) { 180 | // Only output values and halt on errors. Ignore 'info' and 'value-set' 181 | // message types. 182 | if (res.msgType === "value") { 183 | console.log(res.msg); 184 | } else if (res.msgType == "error") { 185 | console.error(res.msg); 186 | process.exit(1); 187 | } else if (res.msgType == "quit") { 188 | process.exit(0); 189 | } 190 | } 191 | }); 192 | } 193 | }()); 194 | -------------------------------------------------------------------------------- /insect.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=Insect 4 | GenericName=Scientific calculator 5 | TryExec=insect 6 | Exec=insect 7 | Icon=insect 8 | Terminal=true 9 | Categories=Education;Science;Math; 10 | Keywords=math;numerical computation;physics;quantities;units 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "insect", 3 | "version": "5.9.0", 4 | "description": "High precision scientific calculator with support for physical units", 5 | "author": "David Peter <mail@david-peter.de>", 6 | "license": "MIT", 7 | "type": "module", 8 | "engines": { 9 | "node": ">=10" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/sharkdp/insect.git" 14 | }, 15 | "homepage": "https://github.com/sharkdp/insect", 16 | "bin": { 17 | "insect": "./index.cjs" 18 | }, 19 | "man": "docs/insect.1", 20 | "directories": { 21 | "test": "test" 22 | }, 23 | "files": [ 24 | "docs/insect.1", 25 | "web/media/insect.svg", 26 | "insect.desktop", 27 | "LICENSE_THIRDPARTY" 28 | ], 29 | "scripts": { 30 | "setup": "npm run build", 31 | "start": "node copy && run-p server esbuild:watch", 32 | "esbuild:watch": "spago build -w --then 'npm run esbuild:browser'", 33 | "esbuild": "npm run esbuild:browser && npm run esbuild:node", 34 | "esbuild:browser": "esbuild output/Insect/index.js --bundle --minify --outfile=web/insect.js --target=es6 --global-name=Insect", 35 | "esbuild:node": "esbuild index.dev.js --bundle --minify --outfile=index.cjs --target=node10 --platform=node --format=iife --external:./node_modules/*", 36 | "build": "spago build && npm run esbuild && node copy", 37 | "prepack": "spago build && npm run esbuild:node", 38 | "server": "live-server web --open", 39 | "test": "spago -x test.dhall test" 40 | }, 41 | "bugs": { 42 | "url": "https://github.com/sharkdp/insect/issues" 43 | }, 44 | "dependencies": { 45 | "clipboardy": "^2.3.0", 46 | "decimal.js": "10.3.1", 47 | "jquery.terminal": "^2.18.0", 48 | "keyboardevent-key-polyfill": "=1.1.0", 49 | "line-reader": "^0.4.0", 50 | "xdg-basedir": "^4.0.0" 51 | }, 52 | "devDependencies": { 53 | "esbuild": "^0.18.0", 54 | "live-server": "^1.2.1", 55 | "npm-run-all": "^4.1.5", 56 | "purescript": "^0.15.2", 57 | "spago": "^0.21.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /packages.dhall: -------------------------------------------------------------------------------- 1 | {- 2 | Need help? See the following resources: 3 | - Spago documentation: https://github.com/purescript/spago 4 | * How to override a package in the package set with a local one: https://github.com/purescript/spago#override-a-package-in-the-package-set-with-a-local-one 5 | * How to override a package in the package set with a remote one: https://github.com/purescript/spago#override-a-package-in-the-package-set-with-a-remote-one 6 | * How to add a package to the package set: https://github.com/purescript/spago#add-a-package-to-the-package-set 7 | - Dhall language tour: https://docs.dhall-lang.org/tutorials/Language-Tour.html 8 | -} 9 | let upstream = 10 | https://github.com/purescript/package-sets/releases/download/psc-0.15.10-20230721/packages.dhall 11 | sha256:8800ac7d0763826544ca3ed3ba61f9dcef761a9e2a1feee0346437d9b861e78f 12 | 13 | in upstream 14 | -------------------------------------------------------------------------------- /spago.dhall: -------------------------------------------------------------------------------- 1 | {- 2 | Need help? See the following resources: 3 | - Spago documentation: https://github.com/purescript/spago 4 | - Dhall language tour: https://docs.dhall-lang.org/tutorials/Language-Tour.html 5 | -} 6 | { name = "insect" 7 | , dependencies = 8 | [ "arrays" 9 | , "bifunctors" 10 | , "control" 11 | , "decimals" 12 | , "either" 13 | , "foldable-traversable" 14 | , "integers" 15 | , "lists" 16 | , "maybe" 17 | , "nonempty" 18 | , "ordered-collections" 19 | , "parsing" 20 | , "prelude" 21 | , "quantities" 22 | , "strings" 23 | , "tuples" 24 | ] 25 | , packages = ./packages.dhall 26 | , sources = [ "src/**/*.purs" ] 27 | } 28 | -------------------------------------------------------------------------------- /src/Insect.purs: -------------------------------------------------------------------------------- 1 | module Insect 2 | ( repl 3 | , initialEnvironment 4 | , supportedUnits 5 | , fmtPlain 6 | , fmtJqueryTerminal 7 | , fmtConsole 8 | , commands 9 | , functions 10 | , identifiers 11 | ) where 12 | 13 | import Prelude 14 | 15 | import Data.Either (Either(..)) 16 | import Data.Map (lookup, keys) 17 | import Data.Set (toUnfoldable) 18 | 19 | import Data.Array (sort) 20 | import Data.Maybe (maybe) 21 | 22 | import Parsing (Position(..), parseErrorPosition, parseErrorMessage) 23 | 24 | import Insect.Parser (Dictionary(..), (==>), 25 | normalUnitDict, imperialUnitDict, parseInsect) 26 | import Insect.Parser as P 27 | import Insect.Interpreter (MessageType(..), Message(..), runInsect) 28 | import Insect.Environment (Environment, StoredValue(..)) 29 | import Insect.Environment as E 30 | import Insect.Format (Formatter, format) 31 | import Insect.Format as F 32 | import Insect.PrettyPrint (prettyQuantity) 33 | 34 | -- | List of all supported units 35 | supportedUnits ∷ Array String 36 | supportedUnits = sort $ toArray normalUnitDict <> toArray imperialUnitDict 37 | <> ["d", "t"] -- see Parser special cases 38 | where 39 | toArray (Dictionary dict) = dict >>= toStrs 40 | toStrs (_ ==> strs) = strs 41 | 42 | -- | Convert a message type to a string. 43 | msgTypeToString ∷ MessageType → String 44 | msgTypeToString Info = "info" 45 | msgTypeToString Error = "error" 46 | msgTypeToString Value = "value" 47 | msgTypeToString ValueSet = "value-set" 48 | 49 | -- | Run Insect, REPL-style. 50 | repl ∷ Formatter → Environment → String → { msg ∷ String 51 | , newEnv ∷ Environment 52 | , msgType ∷ String } 53 | repl fmt env userInput = 54 | case parseInsect env userInput of 55 | Left pErr → 56 | let Position rec = parseErrorPosition pErr 57 | in 58 | { msg: format fmt 59 | [ F.optional (F.text " ") 60 | , F.error $ "Parse error at position " <> 61 | show rec.column <> ": " 62 | , F.text (parseErrorMessage pErr) 63 | ] 64 | , msgType: "error" 65 | , newEnv: env } 66 | Right statement → 67 | let ans = runInsect env statement 68 | in case ans.msg of 69 | Message msgType msg → 70 | { msgType: msgTypeToString msgType 71 | , msg: format fmt msg 72 | , newEnv: ans.newEnv } 73 | MQuit → 74 | { msgType: "quit" 75 | , msg: "" 76 | , newEnv: ans.newEnv } 77 | MCopy → 78 | { msgType: "copy" 79 | , msg: value 80 | , newEnv: ans.newEnv } 81 | where 82 | storedQty (StoredValue _ q) = q 83 | maybeStoredValue = lookup "ans" ans.newEnv.values 84 | value = maybe "" (\sv → format fmtPlain (prettyQuantity $ storedQty sv)) maybeStoredValue 85 | MClear → 86 | { msgType: "clear" 87 | , msg: "" 88 | , newEnv: ans.newEnv } 89 | 90 | -- | Re-export the initial environment 91 | initialEnvironment ∷ Environment 92 | initialEnvironment = E.initialEnvironment 93 | 94 | -- | Re-export the plain formatter 95 | fmtPlain ∷ Formatter 96 | fmtPlain = F.fmtPlain 97 | 98 | -- | Re-export the jquery terminal formatter 99 | fmtJqueryTerminal ∷ Formatter 100 | fmtJqueryTerminal = F.fmtJqueryTerminal 101 | 102 | -- | Re-export the console formatter 103 | fmtConsole ∷ Formatter 104 | fmtConsole = F.fmtConsole 105 | 106 | -- | Re-export the list of commands 107 | commands ∷ Array String 108 | commands = P.commands 109 | 110 | functions ∷ Environment → Array String 111 | functions env = toUnfoldable (keys env.functions) <> ["sum", "product"] 112 | 113 | identifiers ∷ Environment → Array String 114 | identifiers env = toUnfoldable (keys env.values) 115 | -------------------------------------------------------------------------------- /src/Insect/Environment.purs: -------------------------------------------------------------------------------- 1 | module Insect.Environment 2 | ( StorageType(..) 3 | , StoredValue(..) 4 | , MathFunction 5 | , FunctionDescription(..) 6 | , StoredFunction(..) 7 | , Environment 8 | , initialEnvironment 9 | ) where 10 | 11 | import Prelude 12 | 13 | import Data.Bifunctor (lmap) 14 | import Data.Either (Either(..)) 15 | import Data.List (List(..), (:)) 16 | import Data.List.NonEmpty (NonEmptyList(..), length) 17 | import Data.Maybe (Maybe(..)) 18 | import Data.NonEmpty (NonEmpty, (:|)) 19 | import Data.Map (Map, fromFoldable) 20 | import Data.Tuple (Tuple(..)) 21 | 22 | import Quantities (Quantity, ConversionError) 23 | import Quantities as Q 24 | 25 | import Insect.Language (EvalError(..), Identifier, Expression) 26 | import Insect.Functions as F 27 | 28 | -- | Values can be stored as constants, as constants that are not 29 | -- | displayed when calling `list`, and as user-defined quantities. 30 | data StorageType = Constant | HiddenConstant | UserDefined 31 | 32 | derive instance Eq StorageType 33 | 34 | -- | A quantity with a given `StorageType`. 35 | data StoredValue = StoredValue StorageType Quantity 36 | 37 | -- | Mathematical functions on physical quantities. 38 | type MathFunction = Environment → NonEmpty List Quantity → Either EvalError Quantity 39 | 40 | -- | Meta information for a function, mostly for pretty printing 41 | data FunctionDescription = BuiltinFunction (Maybe Int) | UserFunction (NonEmpty List Identifier) Expression 42 | 43 | -- | A mathematical function with a given `StorageType`. 44 | data StoredFunction = StoredFunction StorageType MathFunction FunctionDescription 45 | 46 | -- | The environment consists of identifiers that are mapped to specific 47 | -- | quantities. 48 | type Environment = 49 | { values ∷ Map String StoredValue 50 | , functions ∷ Map String StoredFunction 51 | } 52 | 53 | -- | The initial environment contains a few useful mathematical and physical 54 | -- | constants. 55 | initialEnvironment ∷ Environment 56 | initialEnvironment = 57 | { values: 58 | fromFoldable 59 | [ constVal "alpha" Q.α 60 | , constVal "avogadroConstant" Q.avogadroConstant 61 | , constVal "bohrMagneton" Q.µB 62 | , constVal "boltzmannConstant" Q.kB 63 | , constVal "c" Q.speedOfLight 64 | , constVal "e" Q.e 65 | , constVal "electricConstant" Q.ε0 66 | , constVal "eps0" Q.ε0 67 | , constVal "ε0" Q.ε0 68 | , constVal "elementaryCharge" Q.electronCharge 69 | , constVal "electronCharge" Q.electronCharge 70 | , constVal "electronMass" Q.electronMass 71 | , constVal "G" Q.gravitationalConstant 72 | , constVal "g0" Q.g0 73 | , constVal "goldenRatio" Q.phi 74 | , constVal "φ" Q.phi 75 | , constVal "gravity" Q.g0 76 | , constVal "h_bar" Q.ℏ 77 | , constVal "ℏ" Q.ℏ 78 | , constVal "k_B" Q.kB 79 | , constVal "magneticConstant" Q.µ0 80 | , constVal "mu0" Q.µ0 81 | , constVal "µ0" Q.µ0 82 | , constVal "muB" Q.µB 83 | , constVal "µ_B" Q.µB 84 | , constVal "N_A" Q.avogadroConstant 85 | , constVal "pi" Q.pi 86 | , constVal "π" Q.pi 87 | , constVal "planckConstant" Q.planckConstant 88 | , constVal "protonMass" Q.protonMass 89 | , constVal "speedOfLight" Q.speedOfLight 90 | , constVal "R" Q.idealGasConstant 91 | , constVal "faradayConstant" Q.faradayConstant 92 | 93 | -- Hidden constants 94 | , hiddenVal "hundred" (Q.scalar 1.0e2) 95 | , hiddenVal "thousand" (Q.scalar 1.0e3) 96 | , hiddenVal "million" (Q.scalar 1.0e6) 97 | , hiddenVal "billion" (Q.scalar 1.0e9) 98 | , hiddenVal "trillion" (Q.scalar 1.0e12) 99 | , hiddenVal "quadrillion" (Q.scalar 1.0e15) 100 | , hiddenVal "quintillion" (Q.scalar 1.0e18) 101 | 102 | , hiddenVal "googol" (Q.scalar 1.0e100) 103 | 104 | , hiddenVal "tau" Q.tau 105 | , hiddenVal "τ" Q.tau 106 | ], 107 | functions: 108 | fromFoldable 109 | [ constFunc "abs" (Q.abs >>> pure) 110 | , constFunc "acos" Q.acos 111 | , constFunc "acosh" Q.acosh 112 | , constFunc "acot" Q.acot 113 | , constFunc "arccotangent" Q.acot 114 | , constFunc "acoth" Q.acoth 115 | , constFunc "archypcotangent" Q.acoth 116 | , constFunc "acsc" Q.acsc 117 | , constFunc "arccosecant" Q.acsc 118 | , constFunc "acsch" Q.acsch 119 | , constFunc "archypcosecant" Q.acsch 120 | , constFunc "arcsecant" Q.asec 121 | , constFunc "asech" Q.asech 122 | , constFunc "archypsecant" Q.asech 123 | , constFunc "asin" Q.asin 124 | , constFunc "asinh" Q.asinh 125 | , constFunc "atan" Q.atan 126 | , constFunc2 "atan2" Q.atan2 127 | , constFunc "atanh" Q.atanh 128 | , constFunc "ceil" Q.ceil 129 | , constFunc "cos" Q.cos 130 | , constFunc "cosh" Q.cosh 131 | , constFunc "cot" Q.cot 132 | , constFunc "cotangent" Q.cot 133 | , constFunc "coth" Q.coth 134 | , constFunc "hypcotangent" Q.coth 135 | , constFunc "csc" Q.csc 136 | , constFunc "cosecant" Q.csc 137 | , constFunc "csch" Q.csch 138 | , constFunc "hypcosecant" Q.csch 139 | , constFunc "exp" Q.exp 140 | , constFunc "floor" Q.floor 141 | , constFunc "fromCelsius" F.fromCelsius 142 | , constFunc "fromFahrenheit" F.fromFahrenheit 143 | , constFunc "gamma" Q.gamma 144 | , constFunc "ln" Q.ln 145 | , constFunc "log" Q.ln 146 | , constFunc "log10" Q.log10 147 | , constFuncN "minimum" (lmap QConversionError <<< Q.min <<< NonEmptyList) 148 | , constFuncN "maximum" (lmap QConversionError <<< Q.max <<< NonEmptyList) 149 | , constFuncN "mean" (lmap QConversionError <<< Q.mean <<< NonEmptyList) 150 | , constFunc "round" Q.round 151 | , constFunc "secant" Q.sec 152 | , constFunc "sech" Q.sech 153 | , constFunc "hypsecant" Q.sech 154 | , constFunc "sin" Q.sin 155 | , constFunc "sinh" Q.sinh 156 | , constFunc "sqrt" (Q.sqrt >>> pure) 157 | , constFunc "tan" Q.tan 158 | , constFunc "tanh" Q.tanh 159 | , constFunc "toCelsius" F.toCelsius 160 | , constFunc "toFahrenheit" F.toFahrenheit 161 | ] 162 | } 163 | where 164 | constVal identifier value = Tuple identifier (StoredValue Constant value) 165 | hiddenVal identifier value = Tuple identifier (StoredValue HiddenConstant value) 166 | constFunc identifier func = Tuple identifier (StoredFunction Constant (wrapSimple identifier func) (BuiltinFunction (Just 1))) 167 | constFunc2 identifier func = Tuple identifier (StoredFunction Constant (wrapSimple2 identifier func) (BuiltinFunction (Just 2))) 168 | constFuncN identifier func = Tuple identifier (StoredFunction Constant (const func) (BuiltinFunction Nothing)) 169 | 170 | wrapSimple ∷ Identifier → (Quantity → Either ConversionError Quantity) → MathFunction 171 | wrapSimple name func _ qs = 172 | case qs of 173 | x :| Nil → lmap QConversionError $ func x 174 | _ → Left $ WrongArityError name 1 (length (NonEmptyList qs)) 175 | 176 | wrapSimple2 ∷ Identifier → (Quantity → Quantity → Either ConversionError Quantity) → MathFunction 177 | wrapSimple2 name func _ qs = 178 | case qs of 179 | x1 :| x2 : Nil → lmap QConversionError $ func x1 x2 180 | _ → Left $ WrongArityError name 2 (length (NonEmptyList qs)) 181 | -------------------------------------------------------------------------------- /src/Insect/Format.purs: -------------------------------------------------------------------------------- 1 | -- | A markup format for Insect. 2 | module Insect.Format 3 | ( OutputType 4 | , FormatType 5 | , FormattedString 6 | , Markup 7 | , Formatter 8 | , text 9 | , emph 10 | , error 11 | , val 12 | , ident 13 | , function 14 | , unit 15 | , optional 16 | , nl 17 | , format 18 | , fmtPlain 19 | , fmtJqueryTerminal 20 | , fmtConsole 21 | ) where 22 | 23 | import Prelude 24 | 25 | import Data.Foldable (foldMap) 26 | 27 | data FormatType 28 | = FTText 29 | | FTEmphasized 30 | | FTError 31 | | FTValue 32 | | FTIdentifier 33 | | FTFunction 34 | | FTUnit 35 | 36 | data OutputType 37 | = Normal 38 | | Optional 39 | 40 | -- | A single piece of markup. 41 | data FormattedString = Formatted OutputType FormatType String 42 | 43 | -- | A formatted text. 44 | type Markup = Array FormattedString 45 | 46 | -- | Format as a plain string. 47 | text ∷ String → FormattedString 48 | text = Formatted Normal FTText 49 | 50 | -- | Emphasized text. 51 | emph ∷ String → FormattedString 52 | emph = Formatted Normal FTEmphasized 53 | 54 | -- | Error output. 55 | error ∷ String → FormattedString 56 | error = Formatted Normal FTError 57 | 58 | -- | Format as a value. 59 | val ∷ String → FormattedString 60 | val = Formatted Normal FTValue 61 | 62 | -- | Format as an identifier. 63 | ident ∷ String → FormattedString 64 | ident = Formatted Normal FTIdentifier 65 | 66 | -- | Format as a function name. 67 | function ∷ String → FormattedString 68 | function = Formatted Normal FTFunction 69 | 70 | -- | Format as a physical unit. 71 | unit ∷ String → FormattedString 72 | unit = Formatted Normal FTUnit 73 | 74 | -- | Optional output (whitespace, etc.). 75 | optional ∷ FormattedString → FormattedString 76 | optional (Formatted _ t s) = Formatted Optional t s 77 | 78 | -- | A newline character 79 | nl ∷ FormattedString 80 | nl = text "\n" 81 | 82 | -- | A function that renders the markup to a string representation. 83 | type Formatter = OutputType → FormatType → String → String 84 | 85 | -- | Uncurry a formatter. 86 | uncurry ∷ Formatter → (FormattedString → String) 87 | uncurry fmt (Formatted ot ft s) = fmt ot ft s 88 | 89 | -- | Format an output text with a given formatter. 90 | format ∷ Formatter → Markup → String 91 | format formatter m = foldMap (uncurry formatter) ([ optional nl ] <> m) 92 | 93 | -- | Formatter for plain text output on a command line. 94 | fmtPlain ∷ Formatter 95 | fmtPlain Normal _ s = s 96 | fmtPlain Optional _ _ = "" -- ignore optional output 97 | 98 | jtClass ∷ String → String → String 99 | jtClass _ "" = "" -- do not emit formatting code for empty strings 100 | jtClass name str = "[[;;;hl-" <> name <> "]" <> str <> "]" 101 | 102 | -- | Formatter for rich text output on jquery.terminal. 103 | fmtJqueryTerminal ∷ Formatter 104 | fmtJqueryTerminal _ FTText = identity 105 | fmtJqueryTerminal _ FTEmphasized = jtClass "emphasized" 106 | fmtJqueryTerminal _ FTError = jtClass "error" 107 | fmtJqueryTerminal _ FTValue = jtClass "value" 108 | fmtJqueryTerminal _ FTIdentifier = jtClass "identifier" 109 | fmtJqueryTerminal _ FTFunction = jtClass "function" 110 | fmtJqueryTerminal _ FTUnit = jtClass "unit" 111 | 112 | consoleCode ∷ String → String → String 113 | consoleCode code str = "\x1b[" <> code <> "m" <> str <> "\x1b[0m" 114 | 115 | -- | Formatter for rich text output on a command line. 116 | fmtConsole ∷ Formatter 117 | fmtConsole _ FTText s = s 118 | fmtConsole _ FTEmphasized s = consoleCode ansiBold s 119 | where ansiBold = "01" 120 | fmtConsole _ FTError s = consoleCode ansiRed s 121 | where ansiRed = "31" 122 | fmtConsole _ FTValue s = consoleCode ansiCyan s 123 | where ansiCyan = "36" 124 | fmtConsole _ FTIdentifier s = consoleCode ansiYellow s 125 | where ansiYellow = "33" 126 | fmtConsole _ FTFunction s = consoleCode ansiItalic s 127 | where ansiItalic = "03" 128 | fmtConsole _ FTUnit s = consoleCode ansiGreen s 129 | where ansiGreen = "32" 130 | -------------------------------------------------------------------------------- /src/Insect/Functions.purs: -------------------------------------------------------------------------------- 1 | module Insect.Functions 2 | ( fromCelsius 3 | , toCelsius 4 | , fromFahrenheit 5 | , toFahrenheit 6 | ) where 7 | 8 | import Prelude 9 | 10 | import Data.Either (Either) 11 | 12 | import Quantities (Quantity, ConversionError, (.*)) 13 | import Quantities as Q 14 | 15 | -- | Numerical offset when converting between degree Celsius and Kelvin 16 | offsetCelsius ∷ Number 17 | offsetCelsius = 273.15 18 | 19 | -- | Numerical offset when converting between degree Fahrenheit and Kelvin 20 | offsetFahrenheit ∷ Number 21 | offsetFahrenheit = 459.67 22 | 23 | -- | Numerical multiplier to convert between degree Fahrenheit and Kelvin 24 | multiplierFahrenheit ∷ Number 25 | multiplierFahrenheit = 5.0 / 9.0 26 | 27 | -- | Interpret a scalar value as a value in degree Celsius (°C) and convert it 28 | -- | to Kelvin (K). 29 | -- | Type signature in physical units: scalar => kelvin 30 | fromCelsius ∷ Quantity → Either ConversionError Quantity 31 | fromCelsius tempCelsius' = do 32 | tempCelsius ← Q.toScalar tempCelsius' 33 | pure $ (tempCelsius + offsetCelsius) .* Q.kelvin 34 | 35 | -- | Convert a quantity in Kelvin (K) to a scalar that can be interpreted as 36 | -- | the value in degree Celsius (°C). 37 | -- | Type signature in physical units: kelvin => scalar 38 | toCelsius ∷ Quantity → Either ConversionError Quantity 39 | toCelsius tempKelvin' = do 40 | tempKelvin ← tempKelvin' `Q.asValueIn` Q.kelvin 41 | pure $ Q.scalar (tempKelvin - offsetCelsius) 42 | 43 | -- | Interpret a scalar value as a value in degree Fahrenheit (°F) and convert it 44 | -- | to Kelvin (K). 45 | -- | Type signature in physical units: scalar => kelvin 46 | fromFahrenheit ∷ Quantity → Either ConversionError Quantity 47 | fromFahrenheit tempFahrenheit' = do 48 | tempFahrenheit ← Q.toScalar tempFahrenheit' 49 | pure $ ((tempFahrenheit + offsetFahrenheit) * multiplierFahrenheit) .* Q.kelvin 50 | 51 | -- | Convert a quantity in Kelvin (K) to a scalar that can be interpreted as 52 | -- | the value in degree Celsius (°C). 53 | -- | Type signature in physical units: kelvin => scalar 54 | toFahrenheit ∷ Quantity → Either ConversionError Quantity 55 | toFahrenheit tempKelvin' = do 56 | tempKelvin ← tempKelvin' `Q.asValueIn` Q.kelvin 57 | pure $ Q.scalar (tempKelvin / multiplierFahrenheit - offsetFahrenheit) 58 | -------------------------------------------------------------------------------- /src/Insect/Interpreter.purs: -------------------------------------------------------------------------------- 1 | -- | This module defines the interpreter for Insect. 2 | module Insect.Interpreter 3 | ( MessageType(..) 4 | , Message(..) 5 | , runInsect 6 | ) where 7 | 8 | import Prelude hiding (degree) 9 | 10 | import Data.Array (fromFoldable, singleton) 11 | import Data.Bifunctor (lmap) 12 | import Data.Either (Either(..)) 13 | import Data.Foldable (foldMap, intercalate, foldl) 14 | import Data.Int (round, toNumber) 15 | import Data.List (List(..), sortBy, filter, groupBy, (..), (:)) 16 | import Data.List.NonEmpty (NonEmptyList(..), head, length, zip) 17 | import Data.Map (lookup, insert, delete, toUnfoldable) 18 | import Data.Maybe (Maybe(..)) 19 | import Data.NonEmpty (NonEmpty, (:|)) 20 | import Data.String (toLower) 21 | import Data.Traversable (traverse) 22 | import Data.Tuple (Tuple(..), fst, snd) 23 | import Insect.Environment (Environment, StorageType(..), StoredValue(..), FunctionDescription(..), StoredFunction(..), initialEnvironment, MathFunction) 24 | import Insect.Format (FormattedString, Markup) 25 | import Insect.Format as F 26 | import Insect.Language (BinOp(..), Expression(..), Command(..), Identifier, Statement(..), EvalError(..)) 27 | import Insect.PrettyPrint (pretty, prettyQuantity) 28 | import Quantities (Quantity, ConversionError(..)) 29 | import Quantities as Q 30 | import Control.Apply (lift2) 31 | 32 | -- | A type synonym for error handling. A value of type `Expect Number` is 33 | -- | expected to be a number but might also result in an evaluation error. 34 | type Expect = Either EvalError 35 | 36 | -- | Different kinds of messages that will be returned by the interpreter. 37 | data MessageType = Value | ValueSet | Info | Error 38 | 39 | -- | The output type of the interpreter. 40 | data Message = Message MessageType Markup | MQuit | MCopy | MClear 41 | 42 | -- | Check if the numerical value of a quantity is finite, throw a 43 | -- | `NumericalError` otherwise. 44 | checkFinite ∷ Quantity → Expect Quantity 45 | checkFinite q | Q.isFinite q = pure q 46 | | otherwise = Left NumericalError 47 | 48 | -- | Evaluate a summation or product expression like 49 | -- | `sum(expr, var, low, high)`. 50 | evalSpecial ∷ String 51 | → Environment 52 | → Expression 53 | → Expression 54 | → Expression 55 | → Expression 56 | → Expect Quantity 57 | evalSpecial func env expr (Variable varname) lowExpr highExpr = do 58 | -- Evaluate the expressions `low` and `high` 59 | lowQuantity ← eval env lowExpr 60 | highQuantity ← eval env highExpr 61 | 62 | -- Try to cast to numbers (and round to integers) 63 | low ← lmap QConversionError (round <
gt; Q.toScalar lowQuantity)
 64 |   high ← lmap QConversionError (round <
gt; Q.toScalar highQuantity)
 65 | 
 66 |   let
 67 |     iteration n =
 68 |       eval (env { values = insert varname
 69 |                                   (StoredValue UserDefined (Q.scalar (toNumber n)))
 70 |                                   env.values
 71 |                 }) expr
 72 | 
 73 |     qs = if low > high
 74 |            then Nil
 75 |            else map iteration (low .. high)
 76 | 
 77 |   if func == "sum"
 78 |     then
 79 |       foldl qAdd (Right (Q.scalar 0.0)) qs
 80 |     else -- product
 81 |       foldl (lift2 Q.qMultiply) (Right (Q.scalar 1.0)) qs
 82 | 
 83 |   where
 84 |     qAdd q1' q2' = do
 85 |       q1 ← q1'
 86 |       q2 ← q2'
 87 |       lmap QConversionError (Q.qAdd q1 q2)
 88 | evalSpecial func _ _ _ _ _ = Left (InvalidIdentifier func)
 89 | 
 90 | -- | Evaluate Either.. mathematical expression involving physical quantities.
 91 | eval ∷ Environment → Expression → Expect Quantity
 92 | eval _ (Scalar n)             = pure $ Q.scalar' n
 93 | eval _ (Unit u)               = pure $ Q.quantity 1.0 u
 94 | eval env (Variable name)        = case lookup name env.values of
 95 |                                     Just (StoredValue _ q) → pure q
 96 |                                     Nothing → Left (LookupError name)
 97 | eval env (Factorial x)          = eval env x >>= Q.factorial >>> lmap QConversionError
 98 | eval env (Negate x)             = Q.qNegate <
gt; eval env x
 99 | eval env (Apply name xs)        =
100 |   if name == "sum" || name == "product"
101 |     then
102 |       case xs of
103 |         expr :| var : low : high : Nil →
104 |           evalSpecial name env expr var low high
105 |         _ →
106 |           Left (WrongArityError name 4 (length (NonEmptyList xs)))
107 |     else
108 |       case lookup name env.functions of
109 |         Just (StoredFunction _ fn _) →
110 |           traverse (eval env) xs >>= fn env >>= checkFinite
111 |         Nothing → Left (LookupError name)
112 | eval env (BinOp op x y)         = do
113 |   x' <- eval env x
114 |   y' <- eval env y
115 |   run op x' y' >>= checkFinite
116 |   where
117 |     run ∷ BinOp → Quantity → Quantity → Expect Quantity
118 |     run Sub       a b = qSubtract a b
119 |     run Add       a b = qAdd a b
120 |     run Mul       a b = pure (Q.qMultiply a b)
121 |     run Div       a b = pure (Q.qDivide a b)
122 |     run Pow       a b = Q.pow a <
gt; toScalar b
123 |     run Mod       a b = modulo a b
124 |     run ConvertTo a b = convertTo a b
125 | 
126 |     wrap ∷ ∀ a. Either ConversionError a → Either EvalError a
127 |     wrap = lmap QConversionError
128 | 
129 |     qSubtract q1 q2 = wrap (Q.qSubtract q1 q2)
130 |     qAdd q1 q2 = wrap (Q.qAdd q1 q2)
131 |     toScalar q = wrap (Q.toScalar' q)
132 |     modulo q1 q2 = wrap (Q.modulo q1 q2)
133 |     convertTo source target = wrap (Q.convertTo source (Q.derivedUnit target))
134 | 
135 | -- | Like `eval`, but calls `fullSimplify` on the result if the user did not
136 | -- | ask for an explicit conversion.
137 | evalAndSimplify ∷ Environment → Expression → Expect Quantity
138 | evalAndSimplify env e@(BinOp ConvertTo _ _) = eval env e
139 | evalAndSimplify env e = Q.fullSimplify <
gt; eval env e
140 | 
141 | -- | Render the error message for a conversion error.
142 | conversionErrorMessage ∷ ConversionError → Markup
143 | conversionErrorMessage (ConversionError u1 u2) =
144 |   if u1 == Q.unity
145 |     then
146 |       [ F.error "  Conversion error:", F.nl, F.nl
147 |       , F.text "    Cannot convert a ", F.unit "scalar"
148 |       , F.text " to a quantity of unit ", F.unit (Q.toString u2)
149 |       ]
150 |     else
151 |       if u2 == Q.unity
152 |         then
153 |           [ F.error "  Conversion error:", F.nl, F.nl
154 |           , F.text "    Cannot convert quantity of unit "
155 |           , F.unit (Q.toString u1)
156 |           , F.text " to a ", F.unit "scalar"
157 |           ]
158 |         else
159 |             [ F.error "  Conversion error:", F.nl, F.nl
160 |             , F.text "    Cannot convert unit ", F.unit (Q.toString u1)
161 |             ] <> baseRep u1 <>
162 |             [ F.nl
163 |             , F.text "                to unit ", F.unit (Q.toString u2)
164 |             ] <> baseRep u2
165 |   where
166 |     baseRep u =
167 |       if fst (Q.toStandardUnit u) == Q.unity
168 |         then []
169 |         else
170 |           if Q.toString u == F.format F.fmtPlain usStrs
171 |             then []
172 |             else br
173 |       where
174 |         br = [ F.text " (base units: " ] <> usStrs <> [ F.text ")" ]
175 |         us = Q.baseRepresentation u
176 |         us' = sortBy (comparing Q.toString) us
177 |         usStrs = intercalate [ F.text "·" ] $
178 |                    map (singleton <<< F.unit <<< Q.toString) us'
179 | 
180 | 
181 | -- | Get the error message for an evaluation error.
182 | evalErrorMessage ∷ EvalError → Markup
183 | evalErrorMessage (QConversionError ue) =
184 |   conversionErrorMessage ue
185 | evalErrorMessage (WrongArityError fn expected given) =
186 |   [ F.optional (F.text "  ")
187 |   , F.error "Wrong number of arguments:", F.nl, F.nl
188 |   , F.text "    The function '", F.function fn, F.text "'"
189 |   , F.text " takes ", F.val (show expected)
190 |   , F.text (if expected == 1 then " argument" else " arguments")
191 |   , F.text " (got ", F.val (show given), F.text ")"
192 |   ]
193 | evalErrorMessage (LookupError name) =
194 |   [ F.optional (F.text "  ")
195 |   , F.error "Unknown identifier: "
196 |   , F.ident name]
197 | evalErrorMessage NumericalError =
198 |   [ F.optional (F.text "  ")
199 |   , F.error "Numerical error: "
200 |   , F.text "division by zero or out-of-bounds error" ]
201 | evalErrorMessage (RedefinedConstantError name) =
202 |   [ F.optional (F.text "  ")
203 |   , F.error "Assignment error: ", F.text "'", F.emph name
204 |   , F.text "' cannot be redefined." ]
205 | evalErrorMessage (InvalidIdentifier func) =
206 |   [ F.optional (F.text "  ")
207 |   , F.error "Invalid identifier: ", F.text "second argument of '"
208 |   , F.function func, F.text "' must be a variable name."
209 |   ]
210 | 
211 | -- | Interpreter return type.
212 | type Response = { msg ∷ Message, newEnv ∷ Environment }
213 | 
214 | -- | Show pretty-printed input and the error message.
215 | errorWithInput ∷ Markup → Expression → Environment → EvalError → Response
216 | errorWithInput prefix expr env err =
217 |   { msg: Message Error $ (F.optional <
gt; ([ F.text "  " ] <> prefix <> pretty expr ))
218 |                          <> [ F.optional F.nl, F.optional F.nl ]
219 |                          <> evalErrorMessage err
220 |   , newEnv: env
221 |   }
222 | 
223 | -- | Check whether a given identifier is the name of a constant value/function.
224 | isConstant ∷ Environment → Identifier → Boolean
225 | isConstant env name = isConstantValue || isConstantFunction
226 |   where
227 |     isConstantValue =
228 |       case lookup name env.values of
229 |         Just (StoredValue Constant _)       → true
230 |         Just (StoredValue HiddenConstant _) → true
231 |         _ → false
232 |     isConstantFunction =
233 |       case lookup name env.functions of
234 |         Just (StoredFunction Constant _ _)       → true
235 |         Just (StoredFunction HiddenConstant _ _) → true
236 |         _ → false
237 | 
238 | -- | Format a function definition
239 | prettyPrintFunction ∷ Identifier → NonEmpty List Identifier → Array FormattedString
240 | prettyPrintFunction name argNames =
241 |   [ F.function name, F.text "(" ] <> fArgs <> [ F.text ") = " ]
242 |   where
243 |     fArgs = intercalate [ F.text ", " ] ((\a → [ F.ident a ]) <
gt; argNames)
244 | 
245 | -- | Run a single statement of an Insect program.
246 | runInsect ∷ Environment → Statement → Response
247 | runInsect env (Expression e) =
248 |   case evalAndSimplify env e of
249 |     Left evalErr → errorWithInput [] e env evalErr
250 |     Right value →
251 |       { msg: Message Value $    (F.optional <
gt; ([ F.text "  " ] <> pretty e))
252 |                              <> (F.optional <
gt; [ F.nl, F.nl , F.text "   = " ])
253 |                              <> prettyQuantity value
254 |       , newEnv:
255 |           let storedValue = StoredValue UserDefined value
256 |           in env { values = insert "ans" storedValue (insert "_" storedValue env.values) }
257 |       }
258 | 
259 | runInsect env (VariableAssignment name val) =
260 |   case evalAndSimplify env val of
261 |     Left evalErr → errorWithInput [ F.ident name, F.text " = " ] val env evalErr
262 |     Right value →
263 |       if isConstant env name
264 |         then
265 |           errorWithInput [ F.ident name, F.text " = " ] val env (RedefinedConstantError name)
266 |         else
267 |           { msg: Message ValueSet $
268 |                       (F.optional <
gt; [ F.text "  ", F.ident name, F.text " = " ])
269 |                    <> prettyQuantity value
270 |           , newEnv: env { values = insert name (StoredValue UserDefined value) env.values
271 |                         , functions = delete name env.functions }
272 |           }
273 | 
274 | runInsect env (FunctionAssignment name argNames expr) =
275 |   if isConstant env name
276 |     then
277 |       errorWithInput (prettyPrintFunction name argNames) expr env (RedefinedConstantError name)
278 |     else
279 |       { msg: Message ValueSet $ (F.optional <
gt; ([ F.text "  " ] <> prettyPrintFunction name argNames)) <> pretty expr
280 |       , newEnv: env { functions = insert name (StoredFunction UserDefined userFunc (UserFunction argNames expr)) env.functions
281 |                     , values = delete name env.values
282 |                     }
283 |       }
284 |   where
285 |     argNames' = NonEmptyList argNames
286 |     numExpected = length argNames'
287 | 
288 |     userFunc ∷ MathFunction
289 |     userFunc env' argValues =
290 |       if numGiven == numExpected
291 |         then evalAndSimplify functionEnv expr
292 |         else Left (WrongArityError name numExpected numGiven)
293 |       where
294 |         argValues' = NonEmptyList argValues
295 |         numGiven = length argValues'
296 |         args = zip argNames' argValues'
297 | 
298 |         insertArg map (Tuple argName val) = insert argName (StoredValue UserDefined val) map
299 |         functionEnv = env' { values = foldl insertArg env'.values args
300 |                            , functions = delete name env'.functions
301 |                            }
302 | 
303 | runInsect env (PrettyPrintFunction name) =
304 |   { msg, newEnv: env }
305 |   where
306 |     msg =
307 |       case lookup name env.functions of
308 |         Just (StoredFunction _ _ (BuiltinFunction args)) →
309 |           Message Info [ F.optional (F.text "  "),
310 |                          F.ident name,
311 |                          F.text "(",
312 |                          F.text argText,
313 |                          F.text ") = builtin function" ]
314 |           where
315 |             argText = case args of
316 |                         Just 1 → "x"
317 |                         Just 2 → "x, y"
318 |                         Just _ → "x, y, …"
319 |                         Nothing → "x1, x2, …"
320 |         Just (StoredFunction _ _ (UserFunction args expr)) →
321 |           Message Info $ (F.optional <
gt; ([ F.text "  " ] <> prettyPrintFunction name args)) <> pretty expr
322 |         Nothing → Message Error [ F.text "Unknown function" ]
323 | 
324 | runInsect env (Command Help) = { msg: Message Info
325 |   [ F.emph "insect", F.text " evaluates mathematical expressions that can", F.nl
326 |   , F.text "involve physical quantities. You can start by trying", F.nl
327 |   , F.text "one of these examples:", F.nl
328 |   , F.text "", F.nl
329 |   , F.emph "  > ", F.val "1920", F.text " / ", F.val "16", F.text " * ", F.val "9", F.text "         "
330 |   , F.emph "  > ", F.function "sin", F.text "(", F.val "30", F.text " ", F.unit "deg", F.text ")", F.nl
331 |   , F.text "", F.nl
332 |   , F.emph "  > ", F.val "2", F.text " ", F.unit "min", F.text " + ", F.val "30", F.text " ", F.unit "s", F.text "          "
333 |   , F.emph "  > ", F.val "6", F.text " ", F.unit "Mbit/s", F.text " * ", F.val "1.5", F.text " ", F.unit "h", F.text " -> ", F.unit "GB", F.nl
334 |   , F.text "", F.nl
335 |   , F.emph "  > ", F.text "list", F.text "                  "
336 |   , F.emph "  > ", F.ident "r", F.text " = ", F.val "80", F.text " ", F.unit "cm", F.nl
337 |   , F.emph "  > ", F.val "40000", F.text " ", F.unit "km", F.text " / ", F.ident "c", F.text " -> ", F.unit "ms", F.text "    "
338 |   , F.emph "  > ", F.ident "pi", F.text " * ", F.ident "r", F.text "^", F.val "2", F.text " -> ", F.unit "m", F.text "^", F.val "2", F.nl
339 |   , F.text "", F.nl
340 |   , F.text "Full documentation: https://github.com/sharkdp/insect"
341 |   ], newEnv: env }
342 | 
343 | runInsect env (Command List) =
344 |   { msg: Message Info list, newEnv: env }
345 |   where
346 |     storedValue (StoredValue _ value) = value
347 |     storageType (StoredValue t _) = t
348 |     visibleValues = filter (\e → storageType (snd e) /= HiddenConstant) (toUnfoldable env.values)
349 |     envTuples = sortBy (comparing (_.number <<< Q.prettyPrint' <<< storedValue <<< snd)) visibleValues
350 |     envGrouped = groupBy (\x y → storedValue (snd x) == storedValue (snd y)) envTuples
351 |     envSorted = sortBy (comparing (toLower <<< fst <<< head)) envGrouped
352 |     list = [ F.text "List of variables:", F.nl ] <> foldMap toLine envSorted
353 |     toLine kvPairs =
354 |          [ F.nl, F.text "  " ]
355 |       <> identifiers
356 |       <> [ F.text " = " ]
357 |       <> prettyQuantity val
358 |         where
359 |           identifiers = fromFoldable $ intercalate [ F.text " = " ] $
360 |                           (singleton <<< F.ident <<< fst) <
gt; kvPairs
361 |           val = storedValue (snd (head kvPairs))
362 | 
363 | runInsect _ (Command Reset) =
364 |   { msg: Message Info [F.text "Environment has been reset."]
365 |   , newEnv: initialEnvironment }
366 | 
367 | runInsect _ (Command Quit) = { msg: MQuit, newEnv: initialEnvironment }
368 | 
369 | runInsect env (Command Copy) = { msg: MCopy, newEnv: env }
370 | 
371 | runInsect env (Command Clear) = { msg: MClear, newEnv: env }
372 | 


--------------------------------------------------------------------------------
/src/Insect/Language.purs:
--------------------------------------------------------------------------------
 1 | -- | This module defines the AST for Insect.
 2 | module Insect.Language
 3 |   ( EvalError(..)
 4 |   , Identifier
 5 |   , BinOp(..)
 6 |   , Expression(..)
 7 |   , Command(..)
 8 |   , Statement(..)
 9 |   ) where
10 | 
11 | import Prelude hiding (Unit)
12 | 
13 | import Data.Decimal (Decimal)
14 | import Data.Generic.Rep (class Generic)
15 | import Data.Show.Generic (genericShow)
16 | import Data.List (List)
17 | import Data.NonEmpty (NonEmpty)
18 | import Data.Units (DerivedUnit)
19 | 
20 | import Quantities (ConversionError)
21 | 
22 | -- | Type synonym for identifiers (variable names).
23 | type Identifier = String
24 | 
25 | -- | Binary operators.
26 | data BinOp
27 |  = Add
28 |  | Sub
29 |  | Mul
30 |  | Div
31 |  | Pow
32 |  | Mod
33 |  | ConvertTo
34 | 
35 | derive instance Eq BinOp
36 | derive instance Generic BinOp _
37 | instance Show BinOp where show = genericShow
38 | 
39 | -- | Types of errors that may appear during evaluation.
40 | data EvalError
41 |   = QConversionError ConversionError
42 |   | WrongArityError Identifier Int Int
43 |   | LookupError String
44 |   | NumericalError
45 |   | RedefinedConstantError Identifier
46 |   | InvalidIdentifier String
47 | 
48 | -- | A mathematical expression.
49 | data Expression
50 |  = Scalar Decimal
51 |  | Unit DerivedUnit
52 |  | Variable Identifier
53 |  | Factorial Expression
54 |  | Negate Expression
55 |  | Apply Identifier (NonEmpty List Expression)
56 |  | BinOp BinOp Expression Expression
57 | 
58 | derive instance Eq Expression
59 | instance Show Expression where
60 |   show (Scalar n)          = "(Scalar " <> show n <> ")"
61 |   show (Unit u)            = "(Unit " <> show u <> ")"
62 |   show (Variable n)        = "(Variable " <> show n <> ")"
63 |   show (Factorial x)       = "(Factorial " <> show x <> ")"
64 |   show (Negate x)          = "(Negate " <> show x <> ")"
65 |   show (Apply fn xs)       = "(Apply " <> show fn <> " " <> show xs <> ")"
66 |   show (BinOp op x y)      = "(BinOp " <> show op <> " " <> show x <> " " <> show y <> ")"
67 | 
68 | -- | A command in Insect.
69 | data Command
70 |  = Help
71 |  | Reset
72 |  | List
73 |  | Clear
74 |  | Copy
75 |  | Quit
76 | 
77 | derive instance Eq Command
78 | derive instance Generic Command _
79 | instance Show Command where show = genericShow
80 | 
81 | -- | A statement in Insect.
82 | data Statement
83 |  = Expression Expression
84 |  | VariableAssignment Identifier Expression
85 |  | FunctionAssignment Identifier (NonEmpty List Identifier) Expression
86 |  | PrettyPrintFunction Identifier
87 |  | Command Command
88 | 
89 | derive instance Eq Statement
90 | instance Show Statement where
91 |   show (Expression e)              = "(Expression " <> show e <> ")"
92 |   show (VariableAssignment i e)    = "(VariableAssignment " <> show i <> " " <> show e <> ")"
93 |   show (FunctionAssignment f xs e) = "(FunctionAssignment " <> show f <> " " <> show xs <> " " <> show e <> ")"
94 |   show (PrettyPrintFunction f)     = "(PrettyPrintFunction " <> show f <> ")"
95 |   show (Command c)                 = "(Command " <> show c <> ")"
96 | 


--------------------------------------------------------------------------------
/src/Insect/Parser.purs:
--------------------------------------------------------------------------------
  1 | -- | This module defines the parser for the Insect language.
  2 | module Insect.Parser
  3 |   ( DictEntry(..)
  4 |   , (==>)
  5 |   , Dictionary(..)
  6 |   , commands
  7 |   , prefixDict
  8 |   , normalUnitDict
  9 |   , imperialUnitDict
 10 |   , parseInsect
 11 |   ) where
 12 | 
 13 | import Prelude hiding (degree)
 14 | 
 15 | import Control.Alt ((<|>))
 16 | import Control.Lazy (fix)
 17 | import Data.Array (some, fromFoldable)
 18 | import Data.Decimal (Decimal, fromString, fromNumber, isFinite)
 19 | import Data.Either (Either, isRight)
 20 | import Data.Foldable (traverse_)
 21 | import Data.Foldable as F
 22 | import Data.List (List, many)
 23 | import Data.Map (lookup)
 24 | import Data.Maybe (Maybe(..), fromMaybe)
 25 | import Data.NonEmpty (NonEmpty, (:|))
 26 | import Data.Semigroup.Foldable (foldl1, foldr1)
 27 | import Data.String (fromCodePointArray, codePointFromChar, singleton)
 28 | import Data.Tuple.Nested ((/\))
 29 | import Insect.Environment (Environment, StoredFunction(..))
 30 | import Insect.Language (BinOp(..), Expression(..), Command(..), Statement(..), Identifier)
 31 | import Quantities (DerivedUnit, (./))
 32 | import Quantities as Q
 33 | import Parsing (ParserT, Parser, ParseError, runParser, fail)
 34 | import Parsing.Combinators (option, optionMaybe, try, (<?>), notFollowedBy)
 35 | import Parsing.String (string, char, eof)
 36 | import Parsing.String.Basic (hexDigit, octDigit, oneOf)
 37 | import Parsing.Token (GenLanguageDef(..), LanguageDef, TokenParser, digit, letter, makeTokenParser)
 38 | 
 39 | -- | A type synonym for the main Parser type with `String` as input.
 40 | type P a = Parser String a
 41 | 
 42 | -- | Possible characters for the first character of an identifier.
 43 | identStart ∷ P Char
 44 | identStart = letter <|> char '_'
 45 | 
 46 | -- | Possible characters for identifiers (not for the first character).
 47 | identLetter ∷ P Char
 48 | identLetter = letter <|> digit <|> char '_' <|> char '\''
 49 | 
 50 | -- | A list of allowed commands
 51 | commands ∷ Array String
 52 | commands = ["help", "?", "list", "ls", "ll", "reset", "clear", "cls", "quit", "exit", "copy", "cp"]
 53 | 
 54 | -- | The language definition.
 55 | insectLanguage ∷ LanguageDef
 56 | insectLanguage = LanguageDef
 57 |   { commentStart: ""
 58 |   , commentEnd: ""
 59 |   , commentLine: "#"
 60 |   , nestedComments: false
 61 |   , identStart: identStart
 62 |   , identLetter: identLetter
 63 |   , opStart: oneOf ['+', '-', '*', '·', '⋅', '×', '/', '÷', '%', '^', '!', '→', '➞', '=']
 64 |   , opLetter: oneOf []
 65 |   , reservedNames: commands <> ["¹", "²", "³", "⁴", "⁵", "⁻¹", "⁻²", "⁻³", "⁻⁴", "⁻⁵", "to", "per"]
 66 |   , reservedOpNames: ["->", "+", "-", "*", "·", "⋅", "×", "/", "÷", "%", "^", "!",
 67 |                       "**", "=", ","]
 68 |   , caseSensitive: true
 69 | }
 70 | 
 71 | -- | The actual token parser.
 72 | token ∷ TokenParser
 73 | token = makeTokenParser insectLanguage
 74 | 
 75 | -- | Parse something, inside of parens.
 76 | parens ∷ ∀ a. P a → P a
 77 | parens = token.parens
 78 | 
 79 | -- | Parse one of the reserved operators.
 80 | reservedOp ∷ String → P Unit
 81 | reservedOp = token.reservedOp
 82 | 
 83 | -- | Parse a reserved keyword.
 84 | reserved ∷ String → P Unit
 85 | reserved = token.reserved
 86 | 
 87 | -- | Parse zero or more whitespace characters.
 88 | whiteSpace ∷ P Unit
 89 | whiteSpace = token.whiteSpace
 90 | 
 91 | -- | Parse a number.
 92 | number ∷ P Decimal
 93 | number = do
 94 |   numberPrefix /\ digit' /\ expSymbol ←
 95 |     option ("" /\ digit /\ "e") $ try $ char '0'
 96 |       *> ((char 'x' <|> char 'X') 
gt; ("0x" /\ hexDigit /\ "p")
 97 |       <|> (char 'o' <|> char 'O') 
gt; ("0o" /\ octDigit /\ "p")
 98 |       <|> (char 'b' <|> char 'B') 
gt; ("0b" /\ (oneOf ['0', '1'] <?> "binary digit") /\ "p"))
 99 | 
100 |   numberWithoutPrefix ← number' digit' expSymbol
101 | 
102 |   let floatStr = numberPrefix <> numberWithoutPrefix
103 | 
104 |   case fromString floatStr of
105 |     Just num →
106 |       if isFinite num
107 |       then pure num
108 |       else fail "This number is too large"
109 |     Nothing → fail $ "Parsing of number failed for input '" <> floatStr <> "'"
110 | 
111 | number' ∷ P Char → String → P String
112 | number' digit' expSymbol = do
113 |   decimalPart ← fractionalPart <|> do
114 |     intPart ← digits
115 |     mFracPart ← optionMaybe fractionalPart
116 |     pure (intPart <> fromMaybe "" mFracPart)
117 | 
118 |   expPart ← option "" $ try do
119 |     _ ← string expSymbol
120 |     notFollowedBy identStart
121 |     sad ← signAndDigits
122 |     pure (expSymbol <> sad)
123 | 
124 |   whiteSpace
125 | 
126 |   pure $ decimalPart <> expPart
127 |   where
128 |     digits ∷ P String
129 |     digits = (fromCharArray <<< fromFoldable) <
gt; some digit'
130 | 
131 |     fractionalPart ∷ P String
132 |     fractionalPart = (<>) <
gt; string "." <*> digits
133 | 
134 |     fromCharArray = fromCodePointArray <<< map codePointFromChar
135 | 
136 |     signAndDigits ∷ P String
137 |     signAndDigits = do
138 |       sign ← option '+' (oneOf ['+', '-'])
139 |       intPart ← (fromCharArray <<< fromFoldable) <
gt; some digit
140 |       pure $ singleton (codePointFromChar sign) <> intPart
141 | 
142 | -- | A helper type for entries in the dictionary.
143 | data DictEntry a = DictEntry a (Array String)
144 | 
145 | infix 4 DictEntry as ==>
146 | 
147 | -- | A dictionary of units and their abbreviations.
148 | data Dictionary a = Dictionary (Array (DictEntry a))
149 | 
150 | -- | Build a parser from a Dictionary
151 | buildDictParser ∷ ∀ a. Dictionary a → P a
152 | buildDictParser (Dictionary dict) = F.oneOf $ entryParser <
gt; dict
153 |   where
154 |     entryParser (x ==> abbrevs) = F.oneOf $ abbrevParser x <
gt; abbrevs
155 |     abbrevParser x abbrev = string abbrev *> pure x
156 | 
157 | prefixDict ∷ Dictionary (DerivedUnit → DerivedUnit)
158 | prefixDict = Dictionary
159 |   [ Q.kibi ==> ["kibi", "Ki"]
160 |   , Q.mebi ==> ["mebi", "Mi"]
161 |   , Q.gibi ==> ["gibi", "Gi"]
162 |   , Q.tebi ==> ["tebi", "Ti"]
163 |   , Q.pebi ==> ["pebi", "Pi"]
164 |   , Q.exbi ==> ["exbi", "Ei"]
165 |   , Q.zebi ==> ["zebi", "Zi"]
166 |   , Q.yobi ==> ["yobi", "Yi"]
167 |   , Q.atto ==> ["atto", "a"]
168 |   , Q.femto ==> ["femto", "f"]
169 |   -- peta has to be up here (before pico) in order for the prefix ('p') not to
170 |   -- be parsed as 'pico'. This goes for mega through quetta too.
171 |   , Q.peta ==> ["peta"]
172 |   , Q.mega ==> ["mega"]
173 |   , Q.zetta ==> ["zetta"]
174 |   , Q.yotta ==> ["yotta"]
175 |   , Q.ronna ==> ["ronna"]
176 |   , Q.quetta ==> ["quetta"]
177 |   , Q.pico ==> ["pico", "p"]
178 |   , Q.nano ==> ["nano", "n"]
179 |   , Q.quecto ==> ["quecto", "q"]
180 |   , Q.ronto ==> ["ronto", "r"]
181 |   , Q.yocto ==> ["yocto", "y"]
182 |   , Q.zepto ==> ["zepto", "z"]
183 |   , Q.micro ==> [ "micro"
184 |                 , "u" -- u for micro
185 |                 , "µ" -- Micro sign U+00B5
186 |                 , "μ" -- Greek small letter mu U+039C
187 |                 ]
188 |   , Q.milli ==> ["milli", "m"]
189 |   , Q.centi ==> ["centi", "c"]
190 |   , Q.deci ==> ["deci", "d"]
191 |   , Q.hecto ==> ["hecto", "h"]
192 |   , Q.kilo ==> ["kilo", "k"]
193 |   , Q.mega ==> ["M"]
194 |   , Q.giga ==> ["giga", "G"]
195 |   , Q.tera ==> ["tera", "T"]
196 |   , Q.peta ==> ["P"]
197 |   , Q.exa ==> ["exa", "E"]
198 |   , Q.zetta ==> ["Z"]
199 |   , Q.yotta ==> ["Y"]
200 |   , Q.ronna ==> ["R"]
201 |   , Q.quetta ==> ["Q"]
202 |   ]
203 | 
204 | -- | Parse a SI or IEC prefix like `µ`, `G`, `pico` or `Ki`.
205 | prefix ∷ P (DerivedUnit → DerivedUnit)
206 | prefix = buildDictParser prefixDict <|> pure identity
207 | 
208 | -- | Normal (SI-conform, non-imperial) units
209 | normalUnitDict ∷ Dictionary DerivedUnit
210 | normalUnitDict = Dictionary
211 |   [ Q.radian ==> ["radians", "radian", "rad"]
212 |   , Q.degree ==> ["degrees", "degree", "deg", "°"]
213 |   , Q.hertz ==> ["hertz", "Hz"]
214 |   , Q.rpm ==> ["RPM", "rpm"]
215 |   , Q.newton ==> ["newtons", "newton", "N"]
216 |   , Q.joule ==> ["joules", "joule", "J"]
217 |   , Q.pascal ==> ["pascals", "pascal", "Pa"]
218 |   , Q.volt ==> ["volts", "volt", "V"]
219 |   , Q.farad ==> ["farads", "farad", "F"]
220 |   , Q.ohm ==> ["ohms", "ohm", "Ω"]
221 |   , Q.sievert ==> ["sieverts", "sievert", "Sv"]
222 |   , Q.weber ==> ["webers", "weber", "Wb"]
223 |   , Q.tesla ==> ["teslas", "tesla", "T"]
224 |   , Q.henry ==> ["henrys", "henries", "henry", "H"]
225 |   , Q.coulomb ==> ["coulombs", "coulomb", "C"]
226 |   , Q.siemens ==> ["siemens", "S"]
227 |   , Q.lumen ==> ["lumens", "lumen", "lm"]
228 |   , Q.lux ==> ["lux", "lx"]
229 |   , Q.becquerel ==> ["becquerels", "becquerel", "Bq"]
230 |   , Q.gray ==> ["grays", "gray", "Gy"]
231 |   , Q.katal ==> ["katals", "katal", "kat"]
232 |   , Q.hectare ==> ["hectares", "hectare", "ha"]
233 |   , Q.tonne ==> ["tonnes", "tonne", "tons", "ton"]
234 |   , Q.electronvolt ==> ["electronvolts", "electronvolt", "eV"]
235 |   , Q.calorie ==> ["calories", "calorie", "cal"]
236 |   , Q.bel ==> ["bels", "bel"]
237 |   , Q.astronomicalUnit ==> ["AU", "au", "astronomicalunits", "astronomicalunit"]
238 |   , Q.parsec ==> ["parsecs", "parsec", "pc"]
239 |   , Q.lightyear ==> ["lightyears", "lightyear", "ly"]
240 |   , Q.barn ==> ["barns", "barn"]
241 |   , Q.bar ==> ["bars", "bar"]
242 |   , Q.angstrom ==> ["angstroms", "angstrom", "Å"]
243 |   , Q.gauss ==> ["gauss"]
244 |   , Q.ampere ==> ["amperes", "ampere", "amps", "amp", "A"]
245 |   , Q.molal ==> ["molals", "molal"]
246 |   , Q.molar ==> ["molars", "molar"]
247 |   , Q.mole ==> ["moles", "mole", "mol"]
248 |   , Q.kelvin ==> ["kelvins", "kelvin", "K"]
249 |   , Q.candela ==> ["candelas", "candela", "cd"]
250 |   , Q.watt <> Q.hour ==> ["Wh"]
251 |   , Q.watt ==> ["watts", "watt", "W"]
252 |   , Q.byte ==> ["Bytes", "bytes", "Byte", "byte", "B", "Octets", "octets", "Octet", "octet"]
253 |   , Q.bit ==> ["bits", "bit"]
254 |   , Q.bit ./ Q.second ==> ["bps"]
255 |   , Q.second ==> ["seconds", "second", "sec", "s"]
256 |   , Q.minute ==> ["minutes", "minute", "min"]
257 |   , Q.hour ==> ["hours", "hour", "hr", "h"]
258 |   , Q.day ==> ["days", "day"]
259 |   , Q.week ==> ["weeks", "week"]
260 |   , Q.fortnight ==> ["fortnights", "fortnight"]
261 |   , Q.month ==> ["months", "month"]
262 |   , Q.year ==> ["years", "year"]
263 |   , Q.julianYear ==> ["julianYears", "julianYear"]
264 |   , Q.gram ==> ["grammes", "gramme", "grams", "gram", "g"]
265 |   , Q.meter ==> ["metres", "metre", "meters", "meter", "m"]
266 |   , Q.liter ==> ["liters", "liter", "litres", "litre", "L", "l"]
267 |   , Q.atm ==> ["atm"]
268 |   , Q.pixel ==> ["pixels", "pixel", "px"]
269 |   , Q.frame ==> ["frames", "frame"]
270 |   , Q.frame ./ Q.second ==> ["fps"]
271 |   , Q.dot ==> ["dots", "dot"]
272 |   ]
273 | 
274 | -- | Parse a normal (SI-conform, non-imperial) unit, like `N` or `watt`.
275 | normalUnit ∷ P DerivedUnit
276 | normalUnit = buildDictParser normalUnitDict <?> "normal unit"
277 | 
278 | -- | Imperial units
279 | imperialUnitDict ∷ Dictionary DerivedUnit
280 | imperialUnitDict = Dictionary
281 |   [ Q.percent ==> ["pct", "percent"]
282 |   , Q.partsPerMillion ==> ["ppm"]
283 |   , Q.partsPerBillion ==> ["ppb"]
284 |   , Q.partsPerTrillion ==> ["ppt"]
285 |   , Q.partsPerQuadrillion ==> ["ppq"]
286 |   , Q.mile ==> ["miles", "mile"]
287 |   , Q.mile ./ Q.hour ==> ["mph"]
288 |   , Q.inch ==> ["inches", "inch", "in"]
289 |   , Q.yard ==> ["yards", "yard", "yd"]
290 |   , Q.foot ==> ["feet", "foot", "ft"]
291 |   , Q.thou ==> ["thou", "mils", "mil"]
292 |   , Q.ounce ==> ["ounces", "ounce", "oz"]
293 |   , Q.lbf ==> ["pound_force", "lbf"]
294 |   , Q.pound ==> ["pounds", "pound", "lb"]
295 |   , Q.gallon ==> ["gallons", "gallon", "gal"]
296 |   , Q.pint ==> ["pints", "pint"]
297 |   , Q.cup ==> ["cups", "cup"]
298 |   , Q.tablespoon ==> ["tablespoons", "tablespoon", "tbsp"]
299 |   , Q.teaspoon ==> ["teaspoons", "teaspoon", "tsp"]
300 |   , Q.fluidounce ==> ["fluidounces", "fluidounce", "floz"]
301 |   , Q.furlong ==> ["furlongs", "furlong"]
302 |   , Q.btu ==> ["BTU"]
303 |   , Q.psi ==> ["psi"]
304 |   , Q.mmHg ==> ["mmHg"]
305 |   , Q.hogshead ==> ["hogsheads", "hogshead"]
306 |   , Q.rod ==> ["rods", "rod"]
307 |   , Q.pixel ./ Q.inch ==> ["ppi"]
308 |   , Q.dot ./ Q.inch ==> ["dpi"]
309 |   , Q.piece ==> ["pieces", "piece"]
310 |   , Q.person ==> ["persons", "person", "people"]
311 |   , Q.dollar ==> ["dollars", "dollar", "USD", "
quot;]
312 |   , Q.euro ==> ["euros", "euro", "EUR", "€"]
313 |   , Q.knot ==> ["knots", "knot", "kn", "kt"]
314 |   , Q.nauticalMile ==> ["M", "NM", "nmi"]
315 |   ]
316 | 
317 | -- | Parse an imperial unit like `ft` of `mile`.
318 | imperialUnit ∷ P DerivedUnit
319 | imperialUnit = buildDictParser imperialUnitDict <?> "imperial unit"
320 | 
321 | -- | Parse a 'normal' unit with SI prefix, like `km` or `Gb`.
322 | unitWithSIPrefix ∷ P DerivedUnit
323 | unitWithSIPrefix = do
324 |   p ← prefix
325 |   u ← normalUnit
326 |   pure $ p u
327 | 
328 | specialCases ∷ P DerivedUnit
329 | specialCases =
330 |   -- The abbreviation 'd' for 'day' needs to be treated separately. Otherwise,
331 |   -- 'cd' will be parsed as 'centi day' instead of 'candela'.
332 |       string "d" *> pure Q.day
333 |   -- Similarly, the abbreviation 't' for 'tonne' needs special treatment.
334 |   -- Otherwise, 'ft' will be parsed as 'femto tonne' instead of 'feet'.
335 |   <|> string "t" *> pure Q.tonne
336 | 
337 | -- | Parse a derived unit, like `km`, `ft`, or `s`.
338 | derivedUnit ∷ P DerivedUnit
339 | derivedUnit =
340 |   (
341 |         try (augment unitWithSIPrefix)
342 |     <|> augment imperialUnit
343 |     <|> augment normalUnit
344 |     <|> augment specialCases
345 |   ) <* whiteSpace
346 |   where
347 |     augment p = p <* notFollowedBy identLetter
348 | 
349 | -- | Parse the name of a variable, like `my_variable'`.
350 | variable ∷ P Expression
351 | variable = Variable <
gt; token.identifier
352 | 
353 | -- | A version of `sepBy1` that returns a `NonEmpty List`.
354 | sepBy1 ∷ ∀ m s a sep. ParserT s m a → ParserT s m sep → ParserT s m (NonEmpty List a)
355 | sepBy1 p sep = do
356 |   a ← p
357 |   as ← many $ sep *> p
358 |   pure (a :| as)
359 | 
360 | -- | Parse a function name and fail if it's not in the environment
361 | function ∷ Environment → P Identifier
362 | function env = do
363 |   name ← token.identifier
364 |   if name == "sum" || name == "product"
365 |     then
366 |       pure name
367 |     else
368 |       case lookup name env.functions of
369 |         Just (StoredFunction _ _ _) → pure name
370 |         Nothing → fail ("Unknown function '" <> name <> "'")
371 | 
372 | -- | Parse a full mathematical expression.
373 | expression ∷ Environment → P Expression
374 | expression env =
375 |   fix \p →
376 |     let
377 |       atomic ∷ P Expression
378 |       atomic = whiteSpace *> (
379 |               parens p
380 |           <|> (Scalar <
gt; number)
381 |           <|> try (Unit <
gt; derivedUnit)
382 |           <|> try (Apply <
gt; function env <*> parens (sepBy1 p commaOp))
383 |           <|> variable
384 |           )
385 | 
386 |       suffixFac ∷ P Expression
387 |       suffixFac = do
388 |         a ← atomic
389 |         mf ← optionMaybe (facOp *> pure Factorial)
390 |         pure case mf of
391 |           Just f → f a
392 |           Nothing → a
393 | 
394 |       suffixPow ∷ P Expression
395 |       suffixPow = do
396 |         x ← suffixFac
397 |         mFn ← optionMaybe (     reservedOp "¹" *>  pure (powPos 1.0)
398 |                             <|> reservedOp "²" *>  pure (powPos 2.0)
399 |                             <|> reservedOp "³" *>  pure (powPos 3.0)
400 |                             <|> reservedOp "⁴" *>  pure (powPos 4.0)
401 |                             <|> reservedOp "⁵" *>  pure (powPos 5.0)
402 |                             <|> reservedOp "⁻¹" *> pure (powNeg 1.0)
403 |                             <|> reservedOp "⁻²" *> pure (powNeg 2.0)
404 |                             <|> reservedOp "⁻³" *> pure (powNeg 3.0)
405 |                             <|> reservedOp "⁻⁴" *> pure (powNeg 4.0)
406 |                             <|> reservedOp "⁻⁵" *> pure (powNeg 5.0)
407 |                           )
408 |         pure case mFn of
409 |           Just fn → fn x
410 |           Nothing → x
411 | 
412 |       -- The power operator needs special treatment
413 |       -- 1) it is right-associative => use foldr1 instead of foldl1
414 |       -- 2) the unary minus on the *left* side of the power operator has
415 |       --    a lower precedence, i.e. -2^3 = -(2^3). However, unary minus
416 |       --    on the *right* side has a higher precedence: 2^-3 = 2^(-3).
417 |       sepByPow ∷ P Expression
418 |       sepByPow = fix \e → foldr1 (BinOp Pow) <
gt; list e
419 |         where
420 |           list e = do
421 |             a ← suffixPow
422 |             as ← many do
423 |               powOp
424 |               func ← (subOp *> pure Negate) <|> (addOp *> pure identity) <|> pure identity
425 |               expr ← e
426 |               pure (func expr)
427 |             pure (a :| as)
428 | 
429 |       sepByMulImplicit ∷ P Expression
430 |       sepByMulImplicit = foldl1 (BinOp Mul) <
gt; sepByPow `sepBy1` pure unit
431 | 
432 |       prefixed ∷ P Expression
433 |       prefixed = fix \e →
434 |             (subOp *> (Negate <
gt; e))
435 |         <|> (addOp *> e)
436 |         <|> sepByMulImplicit
437 | 
438 |       sepByMod ∷ P Expression
439 |       sepByMod = foldl1 (BinOp Mod) <
gt; prefixed `sepBy1` modOp
440 | 
441 |       sepByPer ∷ P Expression
442 |       sepByPer = foldl1 (BinOp Div) <
gt; sepByMod `sepBy1` perOp
443 | 
444 |       sepByDiv ∷ P Expression
445 |       sepByDiv = foldl1 (BinOp Div) <
gt; sepByPer `sepBy1` divOp
446 | 
447 |       sepByMul ∷ P Expression
448 |       sepByMul = foldl1 (BinOp Mul) <
gt; sepByDiv `sepBy1` mulOp
449 | 
450 |       sepBySub ∷ P Expression
451 |       sepBySub = foldl1 (BinOp Sub) <
gt; sepByMul `sepBy1` subOp
452 | 
453 |       sepByAdd ∷ P Expression
454 |       sepByAdd = foldl1 (BinOp Add) <
gt; sepBySub `sepBy1` addOp
455 | 
456 |       sepByConv ∷ P Expression
457 |       sepByConv = foldl1 (BinOp ConvertTo) <
gt; sepByAdd `sepBy1` arrOp
458 | 
459 |     in sepByConv
460 | 
461 |   where
462 | 
463 |     commaOp = reservedOp ","
464 |     facOp = reservedOp "!"
465 |     powOp = reservedOp "^" <|> reservedOp "**"
466 |     modOp = reservedOp "%"
467 |     perOp = reserved "per"
468 |     divOp = reservedOp "/" <|> reservedOp "÷"
469 |     mulOp = reservedOp "*" <|> reservedOp "·" <|> reservedOp "⋅"
470 |                            <|> reservedOp "×"
471 |     subOp = reservedOp "-"
472 |     addOp = reservedOp "+"
473 |     arrOp = reservedOp "->" <|> reservedOp "→" <|> reservedOp "➞" <|> reserved "to"
474 | 
475 |     powPos s q | s == 1.0  = q
476 |                | otherwise = BinOp Pow q (Scalar $ fromNumber s)
477 |     powNeg s q = BinOp Pow q (Negate $ Scalar $ fromNumber s)
478 | 
479 | -- | Parse a mathematical expression (or conversion) like `3m+2in -> cm`.
480 | fullExpression ∷ Environment → P Expression
481 | fullExpression env = whiteSpace *> expression env <* (eof <?> "end of input")
482 | 
483 | -- | Parse an Insect command.
484 | command ∷ P Command
485 | command =
486 |   (
487 |         (reserved "help" <|> reserved "?") *> pure Help
488 |     <|> (reserved "list" <|> reserved "ls" <|> reserved "ll") *> pure List
489 |     <|> (reserved "reset") *> pure Reset
490 |     <|> (reserved "clear" <|> reserved "cls") *> pure Clear
491 |     <|> (reserved "copy" <|> reserved "cp") *> pure Copy
492 |     <|> (reserved "quit" <|> reserved "exit") *> pure Quit
493 |   ) <* eof
494 | 
495 | -- | Parse a variable- or function assignment.
496 | assignment ∷ Environment → P Statement
497 | assignment env = do
498 |   { name, args, expr } ←
499 |     try do
500 |       whiteSpace
501 |       name ← token.identifier
502 |       args ← optionMaybe (parens (sepBy1 token.identifier (reservedOp ",")))
503 |       reservedOp "="
504 |       expr ← expression env
505 |       eof
506 | 
507 |       pure { name, args, expr }
508 | 
509 |   failIfReserved name
510 | 
511 |   case args of
512 |     Nothing → pure (VariableAssignment name expr)
513 |     Just xs → do
514 |       traverse_ failIfReserved xs
515 |       pure (FunctionAssignment name xs expr)
516 | 
517 |   where
518 |     failIfReserved n = do
519 |       when (isRight $ runParser n (derivedUnit <* eof)) $
520 |         fail ("'" <> n <> "' is reserved for a physical unit")
521 | 
522 |       when (n == "_" || n == "ans") $
523 |         fail ("'" <> n <> "' is a reserved variable name")
524 | 
525 | -- | Parse a statement in the Insect language.
526 | statement ∷ Environment → P Statement
527 | statement env =
528 |       (Command <
gt; command)
529 |   <|> assignment env
530 |   <|> (try (whiteSpace *> (PrettyPrintFunction <
gt; function env) <* eof))
531 |   <|> (Expression <
gt; fullExpression env)
532 | 
533 | -- | Run the Insect-parser on a `String` input.
534 | parseInsect ∷ Environment → String → Either ParseError Statement
535 | parseInsect env inp = runParser inp (statement env)
536 | 


--------------------------------------------------------------------------------
/src/Insect/PrettyPrint.purs:
--------------------------------------------------------------------------------
  1 | -- | This module defines a pretty printer for Insect expressions.
  2 | module Insect.PrettyPrint
  3 |   ( pretty
  4 |   , prettyQuantity
  5 |   ) where
  6 | 
  7 | import Prelude
  8 | 
  9 | import Data.Decimal as D
 10 | import Data.Semigroup.Foldable (intercalateMap)
 11 | import Data.List (List)
 12 | import Data.NonEmpty (NonEmpty)
 13 | 
 14 | import Quantities as Q
 15 | 
 16 | import Insect.Language (Identifier, BinOp(..), Expression(..))
 17 | import Insect.Format (Markup)
 18 | import Insect.Format as F
 19 | 
 20 | -- | Pretty print a single operator.
 21 | prettyOp ∷ BinOp → Markup
 22 | prettyOp op = [ F.text (opToStr op) ]
 23 |   where
 24 |     opToStr Add       = " + "
 25 |     opToStr Sub       = " - "
 26 |     opToStr Mul       = " × "
 27 |     opToStr Div       = " / "
 28 |     opToStr Pow       = "^"
 29 |     opToStr Mod       = " % "
 30 |     opToStr ConvertTo = " ➞ "
 31 | 
 32 | -- | Pretty print a scalar value.
 33 | prettyScalar ∷ D.Decimal → Markup
 34 | prettyScalar n = [ F.val (D.toString n) ]
 35 | 
 36 | -- | Pretty print a physical unit.
 37 | prettyUnit ∷ Q.DerivedUnit → Markup
 38 | prettyUnit u = [ F.unit (Q.toString u) ]
 39 | 
 40 | -- | Pretty print a physical quantity.
 41 | prettyQuantity ∷ Q.Quantity → Markup
 42 | prettyQuantity q =
 43 |     [ F.val rec.number, F.text space, F.unit rec.unit ]
 44 |   where
 45 |     rec = Q.prettyPrint' q
 46 |     space = if rec.space then " " else ""
 47 | 
 48 | -- | Construct and pretty-print a physical quantity
 49 | prettyQuantity' ∷ D.Decimal → Q.DerivedUnit → Markup
 50 | prettyQuantity' s u = prettyQuantity (Q.quantity' s u)
 51 | 
 52 | -- | Pretty print a variable name.
 53 | prettyVariable ∷ String → Markup
 54 | prettyVariable name = [ F.ident name ]
 55 | 
 56 | -- | Petty print a function application.
 57 | prettyApply ∷ Identifier → NonEmpty List Expression → Markup
 58 | prettyApply fn xs = [ F.function fn, F.text "(" ]
 59 |                     <> intercalateMap [ F.text ", " ] pretty xs
 60 |                     <> [ F.text ")" ]
 61 | 
 62 | -- | Add parenthesis.
 63 | parens ∷ Markup → Markup
 64 | parens m = [ F.text "(" ] <> m <> [ F.text ")" ]
 65 | 
 66 | -- | Add parenthesis, if needed - conservative version for exponentiation.
 67 | withParens' ∷ Expression → Markup
 68 | withParens' u@(Unit _)     = pretty u
 69 | withParens' s@(Scalar _)   = pretty s
 70 | withParens' v@(Variable _) = pretty v
 71 | withParens' a@(Apply _ _)  = pretty a
 72 | withParens' x              = parens (pretty x)
 73 | 
 74 | -- | Add parenthesis, if needed - liberal version, can not be used for
 75 | -- | Exponentiation.
 76 | withParens ∷ Expression → Markup
 77 | withParens e@(BinOp Mul (Scalar _) (Unit _)) = pretty e
 78 | withParens e = withParens' e
 79 | 
 80 | -- | Pretty print an Insect expression.
 81 | pretty ∷ Expression → Markup
 82 | pretty (Scalar n)                      = prettyScalar n
 83 | pretty (Unit u)                        = prettyUnit u
 84 | pretty (Variable name)                 = prettyVariable name
 85 | pretty (Factorial x)                   = withParens x <> [F.text "!"]
 86 | pretty (Negate x)                      = [ F.text "-" ] <> withParens x
 87 | pretty (Apply fnName xs)               = prettyApply fnName xs
 88 | -- ConvertTo (->) never needs parens, it has the lowest precedence:
 89 | pretty (BinOp ConvertTo x y)           = pretty x <> prettyOp ConvertTo <> pretty y
 90 | -- Fuse multiplication of a scalar and a unit to a quantity:
 91 | pretty (BinOp Mul (Scalar s) (Unit u)) = prettyQuantity' s u
 92 | -- Leave out parens for multiplication, if possible:
 93 | pretty (BinOp Mul x y) = addP x <> prettyOp Mul <> addP y
 94 |   where
 95 |     addP ex = case ex of
 96 |                 BinOp Pow _ _ → pretty ex
 97 |                 BinOp Mul _ _ → pretty ex
 98 |                 _             → withParens ex
 99 | -- Leave out parens for division, if possible:
100 | pretty (BinOp Div x y) = addPLeft x <> prettyOp Div <> addPRight y
101 |   where
102 |     addPLeft ex = case ex of
103 |                     BinOp Pow _ _ → pretty ex
104 |                     BinOp Mul _ _ → pretty ex
105 |                     _             → withParens ex
106 |     addPRight ex = case ex of
107 |                      BinOp Pow _ _ → pretty ex
108 |                      _             → withParens ex
109 | -- Leave out parens for addition, if possible:
110 | pretty (BinOp Add x y) = addP x <> prettyOp Add <> addP y
111 |   where
112 |     addP ex = case ex of
113 |                 BinOp Pow _ _ → pretty ex
114 |                 BinOp Mul _ _ → pretty ex
115 |                 BinOp Add _ _ → pretty ex
116 |                 _             → withParens ex
117 | -- Leave out parens for subtraction, if possible:
118 | pretty (BinOp Sub x y) = addP x <> prettyOp Sub <> addP y
119 |   where
120 |     addP ex = case ex of
121 |                 BinOp Pow _ _ → pretty ex
122 |                 BinOp Mul _ _ → pretty ex
123 |                 _             → withParens ex
124 | pretty (BinOp op x y) = withParens' x <> prettyOp op <> withParens' y
125 | 


--------------------------------------------------------------------------------
/test.dhall:
--------------------------------------------------------------------------------
1 | let conf = ./spago.dhall
2 | in conf // {
3 |   sources = conf.sources # [ "test/**/*.purs" ]
4 | , dependencies = conf.dependencies # ["aff", "effect", "spec"]
5 | }
6 | 


--------------------------------------------------------------------------------
/web/.htaccess:
--------------------------------------------------------------------------------
1 | AddOutputFilterByType DEFLATE text/html
2 | AddOutputFilterByType DEFLATE application/javascript
3 | AddOutputFilterByType DEFLATE text/css
4 | AddOutputFilterByType DEFLATE image/svg+xml
5 | 


--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
  1 | <!DOCTYPE html>
  2 | <html lang="en">
  3 |   <head>
  4 |     <meta charset="utf-8" />
  5 |     <meta name="viewport" content="width=device-width, initial-scale=1">
  6 |     <meta name="description" content="High-precision scientific calculator with full support for physical units">
  7 |     <meta name="keywords" content="calculator,scientific,math,physics,unit,conversion,quantity,SI,imperial,precision">
  8 |     <meta name="author" content="David Peter">
  9 |     <meta property="og:type" content="website">
 10 |     <meta property="og:image" content="https://insect.sh/media/insect-banner.png">
 11 |     <meta property="og:title" content="insect - scientific calculator">
 12 |     <meta property="og:url" content="https://insect.sh/">
 13 |     <meta property="og:description" content="High precision scientific calculator with full support for physical units.">
 14 |     <meta name="twitter:card" content="summary_large_image">
 15 |     <meta name="twitter:site" content="@sharkdp86">
 16 |     <meta name="twitter:title" content="insect - scientific calculator">
 17 |     <meta name="twitter:description" content="High precision scientific calculator with full support for physical units.">
 18 |     <meta name="twitter:image:src" content="https://insect.sh/media/insect-banner.png">
 19 |     <meta name="twitter:image:width" content="500">
 20 |     <meta name="twitter:image:height" content="250">
 21 |     <meta name="twitter:url" content="https://insect.sh/">
 22 |     <title>insect - scientific calculator</title>
 23 |     <link href="https://fonts.googleapis.com/css?family=Exo+2%7CFira+Mono:400,700" rel="stylesheet">
 24 |     <link href="terminal.css" rel="stylesheet">
 25 |     <link href="main.css" rel="stylesheet">
 26 |     <link rel="icon" type="image/png" href="media/insect-196x196.png" sizes="196x196">
 27 |     <link rel="icon" type="image/png" href="media/insect-32x32.png" sizes="32x32">
 28 |     <link rel="icon" type="image/png" href="media/insect-16x16.png" sizes="16x16">
 29 |     <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="insect.sh scientific calculator">
 30 |     <script src="insect.js" type="text/javascript"></script>
 31 |     <script src="keyboardevent-key-polyfill.js" type="text/javascript"></script>
 32 |     <script src="jquery.min.js" type="text/javascript"></script>
 33 |     <script src="jquery.terminal.min.js" type="text/javascript"></script>
 34 |     <script src="jquery.mousewheel-min.js" type="text/javascript"></script>
 35 |     <script type="text/javascript">
 36 |       // Load KeyboardEvent polyfill for old browsers
 37 |       keyboardeventKeyPolyfill.polyfill();
 38 | 
 39 |       var insectEnv = Insect.initialEnvironment;
 40 |       var clearCommands = ["clear", "cls", "quit", "exit"];
 41 | 
 42 |       function updateUrlQuery(query) {
 43 |         url = new URL(window.location);
 44 |         if (query == null) {
 45 |           url.searchParams.delete('q');
 46 |         } else {
 47 |           url.searchParams.set('q', query);
 48 |         }
 49 | 
 50 |         history.replaceState(null, null, url);
 51 |       }
 52 | 
 53 |       function interpret(line) {
 54 |         // Skip empty lines or line comments
 55 |         var lineTrimmed = line.trim();
 56 |         if (lineTrimmed === "" || lineTrimmed[0] === "#") {
 57 |           return;
 58 |         }
 59 | 
 60 |         // Run insect
 61 |         var res = Insect.repl(Insect.fmtJqueryTerminal)(insectEnv)(line);
 62 |         insectEnv = res.newEnv;
 63 | 
 64 |         // Handle shell commands
 65 |         if (clearCommands.indexOf(res.msgType) >= 0) {
 66 |           // Clear screen:
 67 |           this.clear();
 68 |           return;
 69 |         } else if (res.msgType === "quit") {
 70 |           // Treat as reset:
 71 |           this.clear();
 72 |           insectEnv = Insect.initialEnvironment;
 73 |           return;
 74 |         } else if (res.msgType === "copy") {
 75 |           // Copy result to clipboard:
 76 |           if (res.msg === "") {
 77 |             res.msg = "\nNo result to copy.\n";
 78 |           } else {
 79 |             navigator.clipboard.writeText(res.msg);
 80 |             res.msg = "\nCopied result '" + res.msg + "' to clipboard.\n";
 81 |           }
 82 |         }
 83 | 
 84 |         updateUrlQuery(line);
 85 | 
 86 |         return res.msg;
 87 |       }
 88 | 
 89 |       function emph(str) {
 90 |         return "[[;;;hl-emphasized]" + str + "]";
 91 |       }
 92 | 
 93 |       function colored(col, str) {
 94 |         return "[[;#" + col + ";]" + str + "]";
 95 |       }
 96 | 
 97 |       var visitedBefore = localStorage.getItem("visitedBefore") === "yes";
 98 |       var greeting = "";
 99 |       if (!visitedBefore) {
100 |         greeting = colored("75715E", "Welcome to insect. Type '?' if this is your first visit.");
101 |         localStorage.setItem("visitedBefore", "yes");
102 |       } else {
103 |         greeting = colored("75715E", "Welcome to insect. Enter '?' for help.");
104 |       }
105 | 
106 |       $(document).ready(function() {
107 |         var term = $('#terminal').terminal(interpret, {
108 |           greetings: greeting,
109 |           name: "terminal",
110 |           height: 550,
111 |           prompt: "[[;;;prompt]&gt; ]",
112 |           // clear: false, // do not include 'clear' command
113 |           // exit: false, // do not include 'exit' command
114 |           checkArity: false,
115 |           historySize: 200,
116 |           historyFilter(line) {
117 |             return line.trim() !== "";
118 |           },
119 |           completion(inp, cb) {
120 |             var identifiers = Insect.identifiers(insectEnv);
121 | 
122 |             var keywords =
123 |               identifiers.concat(Insect.functions(insectEnv), Insect.supportedUnits, Insect.commands);
124 | 
125 |             cb(keywords.sort());
126 |           },
127 |           onClear() {
128 |             updateUrlQuery(null);
129 |           }
130 |         });
131 | 
132 |         // evaluate expression in query string if supplied (via opensearch)
133 |         if (location.search) {
134 |           var queryParams = new URLSearchParams(location.search);
135 |           if (queryParams.has("q")) {
136 |             term.exec(queryParams.get("q"));
137 |           }
138 |         }
139 |       });
140 |     </script>
141 |   </head>
142 |   <body>
143 |     <div id="content">
144 |       <svg id="insect-logo" width="260" height="128" viewBox="0 0 77.391 38.1">
145 |         <defs>
146 |           <linearGradient id="a">
147 |             <stop offset="0" stop-color="#f72d4b"/>
148 |             <stop offset=".312" stop-color="#b1076f"/>
149 |             <stop offset="1" stop-color="#078af4"/>
150 |           </linearGradient>
151 |           <linearGradient href="#a" id="b" x1="-2996.72" x2="-1559.018" y1="4654.921" y2="4200.613" gradientTransform="matrix(.09408 0 0 .09408 -16454.439 2288.328)" gradientUnits="userSpaceOnUse" />
152 |           <linearGradient href="#a" id="c" x1="-12403.771" x2="-12550.984" y1="16503.461" y2="9437.118" gradientTransform="matrix(.0249 0 0 .0249 -16447.602 2300.046)" gradientUnits="userSpaceOnUse" />
153 |         </defs>
154 |         <g stroke-width="2.058" aria-label="insect" fill="currentColor">
155 |           <path d="M28.215 28.178q-.052-.42-.105-1.21-.026-.815-.079-1.815l-.052-2.13-.053-2.104q-.026-1-.079-1.79-.026-.814-.105-1.235 0-.027-.026-.132 0-.105-.027-.237v-.21-.158q.395-.315.658-.21.29.105.473.473.185.342.263.868.08.526.106 1.052.052.526.052.974v.605q.053-.448.158-1.053.105-.605.316-1.236.21-.631.5-1.236.315-.631.762-1.105.447-.473 1-.736.578-.263 1.315-.21 1.157.052 1.894.63.762.58 1.183 1.552.447.974.632 2.236.21 1.263.263 2.683.078 1.42.052 2.893-.026 1.447-.026 2.788-.237.053-.474.185-.236.105-.394.078-.158 0-.237-.21-.052-.237.053-.894l.079-1.447q.079-1 .052-2.21 0-1.21-.131-2.498-.132-1.289-.5-2.341-.342-1.078-1-1.736-.657-.684-1.71-.684-1.078.447-1.814 1.684-.71 1.236-1.157 2.893-.421 1.657-.632 3.603-.184 1.92-.21 3.762-.053-.027-.21-.027h-.316l-.316-.052q-.131-.027-.158-.053zM39.894 26.179q.473 0 .868.184.42.184.815.421.395.21.79.395.42.157.894.157.552 0 1.13-.184.605-.21 1.105-.552.5-.368.816-.868.315-.526.315-1.131.027-.184 0-.447 0-.263-.158-.526-.157-.29-.526-.526-.368-.263-1.078-.395-.684-.131-1.736-.131-1.052-.027-2.604.184-.394 0-.763-.21-.368-.237-.657-.553-.29-.342-.474-.71-.184-.395-.184-.737 0-1.026.421-1.657.447-.657 1.131-1.026.684-.394 1.526-.552.841-.158 1.71-.158.394 0 .894.053.526.026.946.21.448.158.737.526.316.369.316 1.026-.053.447-.158.552-.08.08-.263-.052-.158-.158-.395-.421-.237-.263-.579-.5-.315-.263-.789-.394-.447-.132-1.025 0-.605 0-1.21.131-.605.105-1.105.395-.473.29-.79.789-.288.5-.288 1.262 0 .395.341.553.369.131.947.131.605 0 1.342-.026.763-.053 1.552-.053t1.525.132q.737.105 1.289.473.579.342.894 1 .316.657.237 1.71 0 .762-.447 1.446-.42.684-1.105 1.21-.657.5-1.473.79-.815.288-1.578.21-.316-.027-.868-.158-.526-.158-1.052-.421-.5-.263-.868-.631-.368-.395-.368-.921zM49.047 22.128q0-.789.342-1.736.342-.973.92-1.788.606-.842 1.342-1.394.763-.579 1.578-.579.447 0 .868.184.447.184.842.5.42.29.736.684.342.368.526.789.184.42.184.815.027.395-.21.684l-5.55 1.026q-.105.026-.184.263-.079.21-.131.552-.053.342-.106.763l-.052.79q-.027.368-.027.683v.447q0 .737.342 1.21.369.447.921.684.552.237 1.236.316.71.079 1.394.079.684 0 1.29-.053.604-.053.999-.053 0 .106-.08.237-.078.132-.183.263l-.21.21q-.106.08-.132.106-.21.052-.658.158-.42.078-.92.157-.474.08-.895.132-.42.053-.578.053-.816.157-1.42-.027-.606-.184-1.027-.631-.42-.447-.683-1.078-.263-.658-.395-1.394-.132-.737-.158-1.526 0-.79.08-1.526zm3.814-4.497q-.237 0-.631.263-.369.236-.71.631-.316.368-.553.815-.21.421-.131.737.684.079 1.42-.026.736-.106 1.341-.316.605-.237 1-.526.394-.316.368-.631 0-.316-.5-.553-.473-.263-1.604-.394zM57.753 23.812q0-.973.316-2.157.315-1.184.92-2.183.605-1.026 1.5-1.71.894-.684 2.078-.684.605-.079.973.132.368.184.552.605.21.42.263.973.08.526.08 1.105.025.552 0 1.052 0 .473.052.789h-.658q-.026-.158-.105-.737-.053-.605-.21-1.236-.158-.631-.448-1.13-.263-.5-.762-.5-.737 0-1.394.578-.632.579-1.105 1.394-.474.816-.763 1.683-.29.868-.29 1.447.027.658.185 1.447.158.763.5 1.394.368.631.946 1.026.58.394 1.42.236.632 0 1.21-.263.58-.263 1.132-.552.552-.29 1.104-.552.58-.263 1.21-.263 0 .683-.5 1.21-.5.5-1.236.815-.71.315-1.525.473-.79.158-1.342.158-.894 0-1.657-.368-.736-.368-1.288-.973-.553-.632-.868-1.447-.29-.842-.29-1.762zM70.878 18.762q-.237-.08-.657-.053-.395 0-.868.053-.447.026-.868.079-.421.052-.71.052-.132 0-.185-.368-.026-.395-.105-1.052l3.709-.263q.079-.92.105-1.315.026-.395 0-.658 0-.29-.026-.631v-1.131q.684-.105 1.026-.053.368.027.5.184.13.158.104.474 0 .29-.078.736-.053.447-.106 1.079-.026.631.053 1.42l3.156-.105v1.289q-.21 0-.657.026-.447.026-.973.053-.5.026-.947.052-.421.026-.632.105-.21.947-.447 2.026-.236 1.078-.315 2.13-.08 1.052.052 1.973.132.894.658 1.5.526.578 1.499.815.973.21 2.578-.158-.053.394-.316.657-.237.237-.579.395-.315.131-.683.21-.369.053-.658.053-1.341 0-2.13-.474-.79-.5-1.184-1.288-.395-.79-.5-1.789-.105-1.026-.079-2.078.053-1.052.158-2.078.105-1.026.105-1.867z"/>
156 |         </g>
157 |         <path fill="url(#b)" d="M-16670.852 2763.415c-16.3-1.43-37.895-7.418-51.413-14.252-8.155-4.124-17.496-9.574-22.327-13.026l-3.088-2.206.201-2.422c.287-3.457 2.792-8.218 10.199-19.382 8.26-12.449 9.631-14.258 23.313-30.747 12.318-14.848 13.878-16.369 21.395-20.864 17.59-10.517 35.173-11.494 48.418-2.69 5.093 3.387 9.665 8.601 12.292 14.019 4.284 8.836 10.321 29.043 12.71 42.542.86 4.862 1.038 7.438 1.025 14.917-.02 9.864-.627 13.465-3.163 18.667-1.621 3.325-5.486 7.348-8.716 9.07-3.411 1.818-8.857 3.226-12.479 3.226-1.662 0-3.878.378-5.465.933-5.933 2.074-14.769 2.929-22.902 2.215z" transform="matrix(-.07746 .10711 .10711 .07746 -1566.412 1603.983)"/>
158 |         <path fill="url(#c)" d="M-16763.593 2726.152c-1.583-1.808-4.938-32.508-6.528-39.277-11.029-46.945-17.561-83.504-19.662-114.696-.358-5.3-1.602-23.231-1.245-27.141 1.123-12.306 3.317-19.813 6.809-25.21 3.629-5.61 14.242-10.246 18.969-7.918 4.293 2.113 10.662 9.821 14.369 19.195 2.47 6.243 4.12 11.926 7.307 24.097 1.98 7.561 4.723 9.867 6.043 12.04 15.299 19.518 29.721 25.944 47.625 46.056 5.421 10.56 8.647 19.04 2.348 28.193-4.611 6.99-15.108 19.066-20.092 24.196-13.159 25.9-11.931 13.275-19.703 23.574-2.284 3.025-4.712 6.412-6.158 8.288-7.137 9.26-10.744 18.458-13.31 24.248-1.641 3.705-5.093 11.134-5.228 11.284-8.714-5.528-7.808-2.464-11.544-6.93z" transform="matrix(-.07746 .10711 .10711 .07746 -1566.412 1603.983)"/>
159 |         <g fill="currentColor">
160 |           <path d="M7.653 30.363c-.992-1.482-1.212-3.383-.587-5.084.518-1.407 1.576-2.593 3.07-3.48-1.415.006-2.403-.121-2.96-.486l-.038-.026c-1.32-.922-1.637-4.094-1.945-7.163-.186-1.861-.397-3.97-.816-4.496a28.057 28.057 0 00-.515-.626C2.326 7.156-.244 4.067.957 1.987 2.27.1 4.734.397 8.278 2.865c3.855 2.684 9.21 8.113 15.523 15.725.015-.104.03-.201.041-.286-.425-.155-.712-.45-.7-.784.01-.365.374-.665.872-.777-.2-.866-.846-1.776-1.474-2.66-.845-1.181-1.896-2.05-1.669-3.573a.648.648 0 01.11-.433.67.67 0 01.938-.167.672.672 0 01.168.938c-.17.242-.557.282-.826.205.085 1.174.867 1.786 1.594 2.806.621.87 1.26 1.766 1.508 2.66.727-1.474 1.518-1.989 2.016-2.766.623-.976.986-1.522.966-2.396-.163.006-.513.034-.655-.067a.793.793 0 01-.197-1.108.793.793 0 011.104-.197c.314.219.405.622.26.963-.323 2.032-.453 1.921-1.15 3.017-.495.772-1.284 1.284-2.006 2.75.643.082 1.129.452 1.116.887-.008.314-.282.581-.679.72.119.594.208 1.438.398 2.137.353 1.29-.11 7.248-.51 7.597-.223.14-.51-.695-.774-1.86-.039.247-.079.472-.114.662-.065.27-1.653 6.54-4.76 8.535-1.729 2.29-3.99 2.563-6.544.786-1.626-1.136-3.322-3.037-5.18-5.816zm.032-26.647C4.704 1.641 2.727 1.258 1.832 2.54c-.817 1.418 1.645 4.377 2.829 5.8.198.239.38.454.528.64.58.728.771 2.389 1.036 5.04.243 2.434.577 5.768 1.505 6.417l-.296.425.31-.416c.637.418 2.84.348 5.156.199.904-.197 3.044-.332 5.23-.345 1.91-.083 3.639-.056 4.839.26.221.036.42.075.592.12l.066-.67C17.1 12.068 11.59 6.435 7.685 3.716zm10.916 30.982l.06-.083.087-.053c2.803-1.711 4.368-7.893 4.377-7.921.324-1.807.54-3.94.428-4.705a1.772 1.772 0 01-.199-.134 2.796 2.796 0 00-.695-.262c-.988-.137-2.618-.21-4.295-.213-1.199.05-2.47.141-3.686.23-.568.041-1.101.08-1.61.113-2.574.737-4.358 2.145-5.029 3.966a4.615 4.615 0 00.474 4.149c.659.982 2.656 3.968 4.913 5.541 2.128 1.483 3.772 1.282 5.174-.628z"/>
161 |           <path d="M8.661 6.976c-.136 1.075-1.267 2.121-2.323 1.518-.95-.527-1.44-1.84-1.827-2.707-.243-.548-2.82-1.48-1.717-2.694C3.9 1.881 6.54 4.163 7.295 4.816c.675.542 1.499 1.244 1.366 2.16zM12.355 16.097c2.422.631 4.968 1.087 7.36 1.91.425.15-1.007 1.247-2.208 1.283-1.069.08-7.945 1.012-9.06-.367-1.115-1.38-.56-2.91.979-3.345.946-.275 1.992.268 2.929.52zM15.704 15.001c-1.15-.62-2.22-1.499-3.21-2.267-1.05-.76-1.98-1.539-2.655-2.667-.676-1.13.712-2.311.712-2.311s7.057 6.97 8.698 8.315c-.81.534-2.828-.658-3.545-1.07zM8.616 11.118c-1.077.606-1.974-.016-1.438-1.254s2.85.408 1.438 1.254zM9.329 14.463c-.535.74-1.024-.012-1.024-.012l-.048-.387c.069-.937 1.84-.594 1.072.4zM7.504 12.834c.05-.68 1.337-.432.78.29-.39.538-.745-.009-.745-.009zM8.428 15.71c-.389.538-.743-.008-.743-.008l-.038-.282c.05-.68 1.337-.432.781.29zM6.894 11.78c-.238.328-.455-.005-.455-.005l-.02-.172c.029-.415.816-.263.475.177zM6.994 12.547c-.237.328-.453-.006-.453-.006l-.023-.171c.03-.417.815-.264.476.177zM7.085 13.33c-.238.327-.455-.008-.455-.008l-.022-.17c.03-.415.815-.265.477.177zM7.105 14.276c-.237.328-.454-.005-.454-.005l-.021-.172c.032-.414.817-.263.475.177zM7.933 14.04c-.237.328-.455-.006-.455-.006l-.021-.172c.03-.416.815-.264.476.178zM7.707 12.01c-.237.328-.452-.005-.452-.005l-.022-.172c.03-.414.815-.262.474.177zM9.276 12.232c.147-.427 2.102.868 2.618 1.188.61.335 1.3.78 1.836 1.22-.962.372-2.417-.416-3.356-.666-.521-.166-1.45-1.039-1.098-1.742zM10.599 29.432c-.592-.214-1.526-1.677-1.102-3.195.426-1.516 1.083-1.936 2.075-2.498 1.913-1.172 4.733-1.535 6.927-1.547 1.02-.009 2.042.2 3.096.214-1.813 1.034-2.846 2.901-4.463 4.182-1.532 1.237-4.367 3.612-6.533 2.844z"/>
162 |           <path d="M10.132 30.374c-.012-.987 4.177 1.155 5.544.81 1.368-.342 2.326-1.363 3.347-1.743.766-.246 1.585-.285 1.631.758.04 1.201-.6 1.86-1.317 2.779-.642.815-1.823 1.743-2.603 2.275-1.607.923-6.584-3.47-6.602-4.88z"/>
163 |           <path d="M14.787 30.018c-.24-.568 3.358-3.281 4.822-4.567.486-.45 2.39-2.962 2.906-2.056.429.735-.247 3.392-.808 3.946-.697.668-6.678 3.244-6.92 2.677z"/>
164 |         </g>
165 |         <g fill="none" stroke="currentColor">
166 |           <path stroke-width=".32902091" d="M25.306 20.195s.837.578 1.292.802c.5.246 1.37.835 1.563.59"/>
167 |           <path stroke-width=".32902091" d="M25.399 21.757c.97-1.49 1.248-1.828 1.997-1.263.328.246.776.38.776.38"/>
168 |           <path stroke-width=".32214703" d="M25.237 22.618s.552.81.87 1.18c.695.81 1.97 2.4 2.255 2.268M24.931 22.338s.777.596 1.183.868c.68.456 1.868 1.487 2.1 1.276"/>
169 |         </g>
170 |       </svg>
171 |       <p class="desc">high precision scientific calculator with full support for physical units.</p>
172 |       <div id="terminal"></div>
173 |       <p class="links"><a href="https://github.com/sharkdp/insect#documentation">Documentation</a> &middot; <a href="https://github.com/sharkdp/insect">Source code</a> &middot; <a href="/third-party-licenses.txt">Third-party software licenses</a></p>
174 |     </div>
175 |     <a href="https://github.com/sharkdp/insect" class="github-corner" aria-label="View source on Github">
176 |       <svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg>
177 |     </a>
178 |   </body>
179 | </html>
180 | 


--------------------------------------------------------------------------------
/web/main.css:
--------------------------------------------------------------------------------
  1 | html {
  2 |     font-family: 'Exo 2', sans-serif;
  3 |     background-color: #ECF0F1;
  4 | }
  5 | 
  6 | .github-corner svg {
  7 |     fill: #272822;
  8 |     color: #ECF0F1;
  9 | }
 10 | 
 11 | body {
 12 |     font-size: 1.2em;
 13 |     margin: 0px;
 14 |     margin-top: 18px;
 15 | }
 16 | 
 17 | #content {
 18 |     max-width: 90%;
 19 |     width: 850px;
 20 |     padding-left: 20px;
 21 |     padding-right: 20px;
 22 |     margin: 0 auto;
 23 | }
 24 | 
 25 | h1 {
 26 |     font-weight: 400;
 27 |     font-size: 3em;
 28 |     line-height: 100%;
 29 |     margin-bottom: 0;
 30 |     padding: 0;
 31 | }
 32 | 
 33 | p.desc {
 34 |     margin-top: 0.6em;
 35 |     margin-bottom: 1.8em;
 36 | }
 37 | 
 38 | p.links {
 39 |     font-size: 0.8em;
 40 |     text-align: center;
 41 | }
 42 | 
 43 | p.links a {
 44 |     color: #aaa;
 45 | }
 46 | 
 47 | #terminal {
 48 |     border-radius: 4px;
 49 |     box-shadow: 0px 3px 8px #1a1a1a;
 50 | }
 51 | 
 52 | /* jQuery Terminal */
 53 | 
 54 | .terminal {
 55 |     --color: #F8F8F2;
 56 |     --background: #272822;
 57 |     --size: 1.6;
 58 |     --link-color: #F8F8F2;
 59 |     font-family: 'Fira Mono', monospace;
 60 | }
 61 | 
 62 | .terminal, .terminal .terminal-fill {
 63 |     padding: 1.2em;
 64 |     margin-bottom: 2em;
 65 | }
 66 | 
 67 | .terminal,
 68 | .terminal-output > :not(.raw) span,
 69 | .terminal-output > :not(.raw) a,
 70 | .terminal-output > :not(.raw) div,
 71 | .cmd,
 72 | .cmd span,
 73 | .cmd div {
 74 |     font-family: 'Fira Mono', monospace;
 75 | }
 76 | 
 77 | .terminal-output > div {
 78 |     padding-bottom: 1em;
 79 | }
 80 | 
 81 | .terminal-output > div.terminal-command {
 82 |     padding-bottom: 0em;
 83 | }
 84 | 
 85 | .prompt {
 86 |     font-weight: bold;
 87 |     /* workaround for https://github.com/sharkdp/insect/issues/78 */
 88 |     letter-spacing: 1px;
 89 | }
 90 | 
 91 | /* syntax highlighting */
 92 | 
 93 | .hl-emphasized {
 94 |     font-weight: 700 !important;
 95 | }
 96 | 
 97 | .hl-error {
 98 |     color: #F92672 !important;
 99 | }
100 | 
101 | .hl-value {
102 |     color: #66D9EF !important;
103 | }
104 | 
105 | .hl-identifier {
106 |     color: #FD971F !important;
107 | }
108 | 
109 | .hl-function {
110 |     font-style: italic !important;
111 | }
112 | 
113 | .hl-unit {
114 |     color: #A6E22E !important;
115 | }
116 | 
117 | /* Github Badge */
118 | 
119 | .github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}
120 | 
121 | @media (prefers-color-scheme: dark) {
122 |     html {
123 |         background: #1A1A1A;
124 |         color: #ECF0F1;
125 |     }
126 | 
127 |     .github-corner svg {
128 |         fill: #ECF0F1;
129 |         color: #1A1A1A;
130 |     }
131 | 
132 |     #terminal {
133 |         box-shadow: 0px 3px 8px #121212;
134 |     }
135 | 
136 |     #insect-logo {
137 |         color: black;
138 |         filter: drop-shadow(0 0 10px #ECF0F1);
139 |     }
140 | 
141 |     #insect-logo > path {
142 |         display: none;
143 |     }
144 | }
145 | 


--------------------------------------------------------------------------------
/web/media/README.md:
--------------------------------------------------------------------------------
1 | Original butterfly icon made by Freepik from [www.flaticon.com](http://www.flaticon.com/free-icon/butterfly-side-view_42231).
2 | 


--------------------------------------------------------------------------------
/web/media/insect-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sharkdp/insect/d19e4f15f788a4c1bd277e1ef3bd5a5e3228bdd3/web/media/insect-16x16.png


--------------------------------------------------------------------------------
/web/media/insect-196x196.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sharkdp/insect/d19e4f15f788a4c1bd277e1ef3bd5a5e3228bdd3/web/media/insect-196x196.png


--------------------------------------------------------------------------------
/web/media/insect-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sharkdp/insect/d19e4f15f788a4c1bd277e1ef3bd5a5e3228bdd3/web/media/insect-32x32.png


--------------------------------------------------------------------------------
/web/media/insect-banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sharkdp/insect/d19e4f15f788a4c1bd277e1ef3bd5a5e3228bdd3/web/media/insect-banner.png


--------------------------------------------------------------------------------
/web/media/insect-banner.svg:
--------------------------------------------------------------------------------
  1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 | <!-- Created with Inkscape (http://www.inkscape.org/) -->
  3 | 
  4 | <svg
  5 |    xmlns:dc="http://purl.org/dc/elements/1.1/"
  6 |    xmlns:cc="http://creativecommons.org/ns#"
  7 |    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  8 |    xmlns:svg="http://www.w3.org/2000/svg"
  9 |    xmlns="http://www.w3.org/2000/svg"
 10 |    xmlns:xlink="http://www.w3.org/1999/xlink"
 11 |    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 12 |    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 13 |    width="500"
 14 |    height="250"
 15 |    viewBox="0 0 148.82813 74.414059"
 16 |    version="1.1"
 17 |    id="svg8"
 18 |    inkscape:version="0.92.1 r"
 19 |    sodipodi:docname="insect-banner.svg"
 20 |    inkscape:export-filename="/home/shark/pure/insect/media/insect-banner.png"
 21 |    inkscape:export-xdpi="96"
 22 |    inkscape:export-ydpi="96">
 23 |   <defs
 24 |      id="defs2">
 25 |     <linearGradient
 26 |        inkscape:collect="always"
 27 |        id="linearGradient6015">
 28 |       <stop
 29 |          style="stop-color:#f72d4b;stop-opacity:1"
 30 |          offset="0"
 31 |          id="stop6009" />
 32 |       <stop
 33 |          id="stop6011"
 34 |          offset="0.31170031"
 35 |          style="stop-color:#b1076f;stop-opacity:1" />
 36 |       <stop
 37 |          style="stop-color:#078af4;stop-opacity:1"
 38 |          offset="1"
 39 |          id="stop6013" />
 40 |     </linearGradient>
 41 |     <linearGradient
 42 |        inkscape:collect="always"
 43 |        xlink:href="#linearGradient6015"
 44 |        id="linearGradient6073"
 45 |        gradientUnits="userSpaceOnUse"
 46 |        x1="-2996.7202"
 47 |        y1="4654.9209"
 48 |        x2="-1559.0183"
 49 |        y2="4200.6133"
 50 |        gradientTransform="matrix(0.09407842,0,0,0.09407842,-16454.439,2288.3277)" />
 51 |     <linearGradient
 52 |        inkscape:collect="always"
 53 |        xlink:href="#linearGradient6015"
 54 |        id="linearGradient6075"
 55 |        gradientUnits="userSpaceOnUse"
 56 |        x1="-12403.771"
 57 |        y1="16503.461"
 58 |        x2="-12550.984"
 59 |        y2="9437.1182"
 60 |        gradientTransform="matrix(0.02489158,0,0,0.02489158,-16447.602,2300.0458)" />
 61 |   </defs>
 62 |   <sodipodi:namedview
 63 |      id="base"
 64 |      pagecolor="#ecf0f1"
 65 |      bordercolor="#666666"
 66 |      borderopacity="1.0"
 67 |      inkscape:pageopacity="1"
 68 |      inkscape:pageshadow="2"
 69 |      inkscape:zoom="0.64"
 70 |      inkscape:cx="83.664297"
 71 |      inkscape:cy="273.25985"
 72 |      inkscape:document-units="mm"
 73 |      inkscape:current-layer="g6071"
 74 |      showgrid="false"
 75 |      inkscape:window-width="1920"
 76 |      inkscape:window-height="1176"
 77 |      inkscape:window-x="1920"
 78 |      inkscape:window-y="0"
 79 |      inkscape:window-maximized="0"
 80 |      showborder="true"
 81 |      inkscape:snap-global="false"
 82 |      showguides="false"
 83 |      inkscape:guide-bbox="true"
 84 |      fit-margin-top="5"
 85 |      fit-margin-left="5"
 86 |      fit-margin-right="5"
 87 |      fit-margin-bottom="5"
 88 |      inkscape:pagecheckerboard="false"
 89 |      units="px">
 90 |     <sodipodi:guide
 91 |        position="-39.951962,3021.5609"
 92 |        orientation="0,1"
 93 |        id="guide4507"
 94 |        inkscape:locked="false" />
 95 |     <sodipodi:guide
 96 |        position="-137.11484,3100.2277"
 97 |        orientation="0,1"
 98 |        id="guide4509"
 99 |        inkscape:locked="false" />
100 |   </sodipodi:namedview>
101 |   <metadata
102 |      id="metadata5">
103 |     <rdf:RDF>
104 |       <cc:Work
105 |          rdf:about="">
106 |         <dc:format>image/svg+xml</dc:format>
107 |         <dc:type
108 |            rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
109 |         <dc:title></dc:title>
110 |       </cc:Work>
111 |     </rdf:RDF>
112 |   </metadata>
113 |   <g
114 |      inkscape:label="logo"
115 |      inkscape:groupmode="layer"
116 |      id="layer1"
117 |      transform="translate(-212.55597,-2887.9362)">
118 |     <flowRoot
119 |        xml:space="preserve"
120 |        id="flowRoot4625"
121 |        style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:25px;font-family:'Bitstream Vera Sans';-inkscape-font-specification:'Bitstream Vera Sans';letter-spacing:0px;word-spacing:0px;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
122 |        transform="scale(0.26458333)"><flowRegion
123 |          id="flowRegion4627"><rect
124 |            id="rect4629"
125 |            width="989.94946"
126 |            height="56.568542"
127 |            x="1882.9243"
128 |            y="827.55511" /></flowRegion><flowPara
129 |          id="flowPara4631" /></flowRoot>    <g
130 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
131 |        id="g4839" />
132 |     <g
133 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
134 |        id="g4841" />
135 |     <g
136 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
137 |        id="g4843" />
138 |     <g
139 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
140 |        id="g4845" />
141 |     <g
142 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
143 |        id="g4847" />
144 |     <g
145 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
146 |        id="g4849" />
147 |     <g
148 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
149 |        id="g4851" />
150 |     <g
151 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
152 |        id="g4853" />
153 |     <g
154 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
155 |        id="g4855" />
156 |     <g
157 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
158 |        id="g4857" />
159 |     <g
160 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
161 |        id="g4859" />
162 |     <g
163 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
164 |        id="g4861" />
165 |     <g
166 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
167 |        id="g4863" />
168 |     <g
169 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
170 |        id="g4865" />
171 |     <g
172 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
173 |        id="g4867" />
174 |     <g
175 |        id="g6297"
176 |        transform="matrix(0.27096735,0,0,0.27096735,156.04521,2132.9182)">
177 |       <g
178 |          transform="matrix(-0.27496946,0.38022183,0.38022183,0.27496946,-5348.5376,8616.6258)"
179 |          id="g6071">
180 |         <g
181 |            id="g4585"
182 |            transform="matrix(1.7195623,0,0,1.7195623,11828.045,-1876.2361)">
183 |           <g
184 |              transform="matrix(-0.43519353,0.60177622,0.60177622,0.43519353,-18441.976,1096.7623)"
185 |              id="text6019"
186 |              style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:24.6923027px;line-height:51.44228745px;font-family:'Bitstream Vera Sans';-inkscape-font-specification:'Bitstream Vera Sans';letter-spacing:0px;word-spacing:0px;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.05769205px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
187 |              aria-label="insect">
188 |             <path
189 |                inkscape:connector-curvature="0"
190 |                id="path6299"
191 |                style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:274.35894775px;font-family:'Annie Use Your Telescope';-inkscape-font-specification:'Annie Use Your Telescope';fill:#000000;fill-opacity:1;stroke-width:2.05769205px"
192 |                d="m 494.86791,3204.947 q -0.53586,-4.2869 -1.07172,-12.3247 -0.26793,-8.3058 -0.80378,-18.4871 -0.26793,-10.4492 -0.53586,-21.7022 -0.26793,-11.253 -0.53586,-21.4343 -0.26793,-10.1813 -0.80379,-18.2192 -0.26792,-8.3058 -1.07171,-12.5926 0,-0.268 -0.26793,-1.3397 0,-1.0717 -0.26793,-2.4113 0,-1.3397 0,-2.1435 0,-1.0717 0,-1.6075 4.01893,-3.2152 6.69822,-2.1435 2.94721,1.0718 4.82271,4.8228 1.87551,3.483 2.67929,8.8416 0.80379,5.3586 1.07172,10.7171 0.53585,5.3586 0.53585,9.9134 0,4.2869 0,6.1624 0.53586,-4.5548 1.60758,-10.7172 1.07171,-6.1623 3.21514,-12.5926 2.14343,-6.4303 5.09064,-12.5927 3.21515,-6.4303 7.76994,-11.253 4.55478,-4.8227 10.18128,-7.502 5.89443,-2.6793 13.39644,-2.1434 11.78886,0.5358 19.29086,6.4303 7.76993,5.8944 12.05679,15.8078 4.55479,9.9133 6.43029,22.7739 2.14343,12.8606 2.67928,27.3287 0.80379,14.4682 0.53586,29.4722 -0.26793,14.7361 -0.26793,28.4004 -2.41135,0.5359 -4.82271,1.8755 -2.41136,1.0717 -4.01893,0.8038 -1.60757,0 -2.41136,-2.1434 -0.53586,-2.4114 0.53586,-9.1096 0.26793,-4.8227 0.80378,-14.7361 0.80379,-10.1813 0.53586,-22.506 0,-12.3247 -1.33964,-25.4532 -1.33965,-13.1285 -5.09065,-23.8457 -3.48307,-10.985 -10.18129,-17.6832 -6.69821,-6.9662 -17.41536,-6.9662 -10.98507,4.5548 -18.48708,17.1474 -7.23407,12.5927 -11.78886,29.4722 -4.28686,16.8795 -6.43029,36.7062 -1.8755,19.5588 -2.14343,38.3138 -0.53585,-0.2679 -2.14342,-0.2679 -1.33965,0 -3.21515,0 -1.60757,-0.2679 -3.21514,-0.5359 -1.33965,-0.2679 -1.60757,-0.5358 z" />
193 |             <path
194 |                inkscape:connector-curvature="0"
195 |                id="path6301"
196 |                style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:274.35894775px;font-family:'Annie Use Your Telescope';-inkscape-font-specification:'Annie Use Your Telescope';fill:#000000;fill-opacity:1;stroke-width:2.05769205px"
197 |                d="m 613.82823,3184.5844 q 4.82272,0 8.84165,1.8755 4.28686,1.8755 8.30579,4.2869 4.01893,2.1434 8.03786,4.0189 4.28685,1.6076 9.10957,1.6076 5.6265,0 11.52093,-1.8755 6.16236,-2.1435 11.25301,-5.6265 5.09064,-3.751 8.30579,-8.8417 3.21514,-5.3586 3.21514,-11.5209 0.26793,-1.8755 0,-4.5548 0,-2.6793 -1.60757,-5.3586 -1.60757,-2.9472 -5.35858,-5.3586 -3.751,-2.6792 -10.98507,-4.0189 -6.96615,-1.3396 -17.68329,-1.3396 -10.71715,-0.268 -26.52494,1.8755 -4.01893,0 -7.76993,-2.1435 -3.751,-2.4113 -6.69822,-5.6265 -2.94721,-3.483 -4.82271,-7.234 -1.8755,-4.019 -1.8755,-7.502 0,-10.4493 4.28685,-16.8795 4.55479,-6.6983 11.52094,-10.4493 6.96614,-4.0189 15.53986,-5.6265 8.57372,-1.6075 17.41536,-1.6075 4.01893,0 9.10958,0.5358 5.35857,0.268 9.64543,2.1435 4.55479,1.6075 7.502,5.3585 3.21514,3.751 3.21514,10.4492 -0.53585,4.5548 -1.60757,5.6265 -0.80378,0.8038 -2.67928,-0.5358 -1.60758,-1.6076 -4.01893,-4.2869 -2.41136,-2.6793 -5.89443,-5.0906 -3.21515,-2.6793 -8.03786,-4.0189 -4.55479,-1.3397 -10.44922,0 -6.16236,0 -12.32472,1.3396 -6.16236,1.0717 -11.253,4.0189 -4.82272,2.9472 -8.03786,8.0379 -2.94722,5.0906 -2.94722,12.8606 0,4.0189 3.48307,5.6265 3.751,1.3396 9.64544,1.3396 6.16235,0 13.66436,-0.2679 7.76993,-0.5359 15.80779,-0.5359 8.03786,0 15.53986,1.3397 7.502,1.0717 13.1285,4.8227 5.89444,3.4831 9.10958,10.1813 3.21514,6.6982 2.41136,17.4153 0,7.77 -4.55479,14.7361 -4.28686,6.9662 -11.253,12.3247 -6.69822,5.0907 -15.00401,8.0379 -8.30579,2.9472 -16.07572,2.1434 -3.21514,-0.2679 -8.84164,-1.6076 -5.35858,-1.6075 -10.71715,-4.2868 -5.09065,-2.6793 -8.84165,-6.4303 -3.751,-4.0189 -3.751,-9.3775 z" />
198 |             <path
199 |                inkscape:connector-curvature="0"
200 |                id="path6303"
201 |                style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:274.35894775px;font-family:'Annie Use Your Telescope';-inkscape-font-specification:'Annie Use Your Telescope';fill:#000000;fill-opacity:1;stroke-width:2.05769205px"
202 |                d="m 707.0674,3143.3234 q 0,-8.0379 3.48308,-17.6833 3.48307,-9.9134 9.3775,-18.2192 6.16236,-8.5737 13.66436,-14.2002 7.76993,-5.8944 16.07572,-5.8944 4.55479,0 8.84165,1.8755 4.55478,1.8755 8.57371,5.0906 4.28686,2.9473 7.502,6.9662 3.48308,3.751 5.35858,8.0378 1.8755,4.2869 1.8755,8.3058 0.26793,4.019 -2.14343,6.9662 l -56.53295,10.4492 q -1.07171,0.2679 -1.8755,2.6793 -0.80378,2.1434 -1.33964,5.6265 -0.53586,3.4831 -1.07172,7.7699 -0.26793,4.0189 -0.53585,8.0379 -0.26793,3.751 -0.26793,6.9661 0,2.9472 0,4.5548 0,7.502 3.48307,12.3247 3.751,4.5548 9.3775,6.9662 5.62651,2.4113 12.59265,3.2151 7.23407,0.8038 14.20022,0.8038 6.96614,0 13.1285,-0.5359 6.16236,-0.5358 10.18129,-0.5358 0,1.0717 -0.80378,2.4113 -0.80379,1.3397 -1.8755,2.6793 -1.07172,1.0717 -2.14343,2.1435 -1.07172,0.8037 -1.33965,1.0717 -2.14343,0.5358 -6.69821,1.6075 -4.28686,0.8038 -9.37751,1.6076 -4.82271,0.8038 -9.10957,1.3397 -4.28686,0.5358 -5.89443,0.5358 -8.30579,1.6076 -14.46815,-0.2679 -6.16236,-1.8755 -10.44922,-6.4303 -4.28686,-4.5548 -6.96614,-10.9851 -2.67929,-6.6982 -4.01893,-14.2002 -1.33964,-7.502 -1.60757,-15.5399 0,-8.0378 0.80378,-15.5398 z m 38.84966,-45.8158 q -2.41136,0 -6.43029,2.6793 -3.751,2.4113 -7.23407,6.4303 -3.21515,3.751 -5.62651,8.3057 -2.14342,4.2869 -1.33964,7.5021 6.96615,0.8037 14.46815,-0.268 7.502,-1.0717 13.66436,-3.2151 6.16236,-2.4114 10.18129,-5.3586 4.01893,-3.2151 3.751,-6.4303 0,-3.2151 -5.09064,-5.6265 -4.82272,-2.6793 -16.34365,-4.0189 z" />
203 |             <path
204 |                inkscape:connector-curvature="0"
205 |                id="path6305"
206 |                style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:274.35894775px;font-family:'Annie Use Your Telescope';-inkscape-font-specification:'Annie Use Your Telescope';fill:#000000;fill-opacity:1;stroke-width:2.05769205px"
207 |                d="m 795.75181,3160.4708 q 0,-9.9133 3.21514,-21.9701 3.21515,-12.0568 9.3775,-22.2381 6.16236,-10.4492 15.27194,-17.4154 9.10957,-6.9661 21.16636,-6.9661 6.16236,-0.8038 9.91336,1.3396 3.751,1.8755 5.6265,6.1624 2.14343,4.2868 2.67929,9.9133 0.80379,5.3586 0.80379,11.253 0.26793,5.6266 0,10.7172 0,4.8227 0.53585,8.0379 h -6.69821 q -0.26793,-1.6076 -1.07172,-7.502 -0.53585,-6.1624 -2.14343,-12.5927 -1.60757,-6.4303 -4.55478,-11.5209 -2.67929,-5.0907 -7.76993,-5.0907 -7.50201,0 -14.20022,5.8945 -6.43029,5.8944 -11.25301,14.2002 -4.82271,8.3058 -7.76993,17.1474 -2.94721,8.8417 -2.94721,14.7361 0.26793,6.6982 1.8755,14.7361 1.60757,7.7699 5.09064,14.2002 3.751,6.4303 9.64543,10.4492 5.89443,4.0189 14.46815,2.4114 6.43029,0 12.32472,-2.6793 5.89443,-2.6793 11.52093,-5.6265 5.6265,-2.9472 11.25301,-5.6265 5.89443,-2.6793 12.32471,-2.6793 0,6.9661 -5.09064,12.3247 -5.09064,5.0906 -12.59265,8.3058 -7.23407,3.2151 -15.53986,4.8227 -8.03786,1.6076 -13.66436,1.6076 -9.10958,0 -16.87951,-3.751 -7.502,-3.751 -13.1285,-9.9134 -5.6265,-6.4303 -8.84165,-14.7361 -2.94721,-8.5737 -2.94721,-17.9512 z" />
208 |             <path
209 |                inkscape:connector-curvature="0"
210 |                id="path6307"
211 |                style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:274.35894775px;font-family:'Annie Use Your Telescope';-inkscape-font-specification:'Annie Use Your Telescope';fill:#000000;fill-opacity:1;stroke-width:2.05769205px"
212 |                d="m 929.44818,3109.0285 q -2.41135,-0.8038 -6.69821,-0.5358 -4.01893,0 -8.84165,0.5358 -4.55479,0.2679 -8.84165,0.8038 -4.28685,0.5359 -7.23407,0.5359 -1.33964,0 -1.8755,-3.751 -0.26793,-4.019 -1.07171,-10.7172 l 37.77794,-2.6793 q 0.80378,-9.3775 1.07171,-13.3964 0.26793,-4.0189 0,-6.6982 0,-2.9472 -0.26793,-6.4303 0,-3.751 0,-11.5209 6.96615,-1.0718 10.44922,-0.5359 3.751,0.2679 5.09065,1.8755 1.33964,1.6076 1.07171,4.8227 0,2.9472 -0.80379,7.502 -0.53585,4.5548 -1.07171,10.9851 -0.26793,6.4303 0.53586,14.4681 l 32.15144,-1.0717 v 13.1285 q -2.14343,0 -6.69822,0.268 -4.55479,0.2679 -9.91336,0.5358 -5.09065,0.2679 -9.64543,0.5359 -4.28686,0.2679 -6.43029,1.0717 -2.14343,9.6454 -4.55479,20.6305 -2.41136,10.9851 -3.21514,21.7022 -0.80379,10.7172 0.53586,20.0947 1.33964,9.1095 6.69821,15.2719 5.35858,5.8944 15.27194,8.3058 9.91336,2.1434 26.257,-1.6076 -0.53585,4.0189 -3.21514,6.6982 -2.41136,2.4114 -5.89443,4.019 -3.21514,1.3396 -6.96615,2.1434 -3.751,0.5358 -6.69821,0.5358 -13.66436,0 -21.70222,-4.8227 -8.03786,-5.0906 -12.05679,-13.1285 -4.01893,-8.0378 -5.09065,-18.2191 -1.07171,-10.4492 -0.80378,-21.1664 0.53585,-10.7171 1.60757,-21.1663 1.07171,-10.4493 1.07171,-19.023 z" />
213 |           </g>
214 |           <path
215 |              inkscape:connector-curvature="0"
216 |              id="path6023"
217 |              d="m -16670.852,2763.4152 c -16.3,-1.4311 -37.895,-7.4178 -51.413,-14.2525 -8.155,-4.124 -17.496,-9.5735 -22.327,-13.0253 l -3.088,-2.206 0.201,-2.4221 c 0.287,-3.4573 2.792,-8.2186 10.199,-19.3823 8.26,-12.4489 9.631,-14.2576 23.313,-30.7473 12.318,-14.8473 13.878,-16.3687 21.395,-20.8632 17.59,-10.518 35.173,-11.4947 48.418,-2.6897 5.093,3.386 9.665,8.6002 12.292,14.0181 4.284,8.836 10.321,29.0433 12.71,42.5424 0.86,4.8613 1.038,7.4374 1.025,14.9163 -0.02,9.8641 -0.627,13.4656 -3.163,18.667 -1.621,3.3259 -5.486,7.3489 -8.716,9.0702 -3.411,1.818 -8.857,3.2261 -12.479,3.2261 -1.662,0 -3.878,0.3786 -5.465,0.9335 -5.933,2.0741 -14.769,2.9286 -22.902,2.2148 z"
218 |              style="opacity:1;fill:url(#linearGradient6073);fill-opacity:1;stroke:#ffffff;stroke-width:3.61613226;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0" />
219 |           <path
220 |              sodipodi:nodetypes="ccscccssccccccccc"
221 |              inkscape:connector-curvature="0"
222 |              id="path6025"
223 |              d="m -16763.593,2726.1517 c -1.583,-1.8075 -4.938,-32.5076 -6.528,-39.2769 -11.029,-46.9448 -17.561,-83.5036 -19.662,-114.6954 -0.358,-5.3012 -1.602,-23.2314 -1.245,-27.1416 1.123,-12.3057 3.317,-19.8129 6.809,-25.2103 3.629,-5.6098 14.242,-10.2452 18.969,-7.9179 4.293,2.1131 10.662,9.8218 14.369,19.1951 2.47,6.2435 4.12,11.9259 7.307,24.0974 1.98,7.561 4.723,9.8665 6.043,12.0396 15.299,19.5183 29.721,25.944 47.625,46.0566 5.421,10.559 8.647,19.0396 2.348,28.1929 -4.611,6.9892 -15.108,19.0656 -20.092,24.1956 -13.159,25.9004 -11.931,13.2756 -19.703,23.5738 -2.284,3.0252 -4.712,6.4126 -6.158,8.2882 -7.137,9.2601 -10.744,18.4583 -13.31,24.2483 -1.641,3.7044 -5.093,11.1342 -5.228,11.2835 -8.714,-5.5279 -7.808,-2.464 -11.544,-6.9289 z"
224 |              style="opacity:1;fill:url(#linearGradient6075);fill-opacity:1;stroke:#ffffff;stroke-width:1.7601006;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0" />
225 |           <path
226 |              sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
227 |              inkscape:connector-curvature="0"
228 |              d="m -16624.127,2672.9725 c -4.688,-12.6529 -15.368,-22.4252 -28.566,-26.1311 -10.92,-3.0622 -22.879,-1.8406 -34.946,3.384 6.314,-8.6416 9.913,-15.2611 10.144,-20.2958 l 0.01,-0.3467 c 0.207,-12.1824 -17.836,-28.1863 -35.284,-43.6784 -10.582,-9.3927 -22.574,-20.0317 -23.944,-24.9344 -0.479,-1.692 -0.991,-3.6894 -1.552,-5.9261 -4.506,-17.6051 -12.05,-47.046 -30.126,-48.9113 -17.39,-0.3136 -26.494,16.1029 -27.072,48.771 -0.635,35.5321 8.906,92.4246 27.576,164.8671 -0.702,-0.3714 -1.362,-0.7098 -1.932,-1.0151 0.933,-3.2933 0.396,-6.3636 -1.7,-7.775 -2.278,-1.5517 -5.736,-0.652 -8.633,1.9066 -4.416,-5.0678 -7.132,-13.0573 -9.764,-20.8241 -3.5,-10.4161 -4.162,-20.7115 -14.506,-26.0716 -0.86,-0.7304 -1.923,-1.2353 -3.147,-1.2461 -2.815,-0.054 -5.135,2.1806 -5.178,5.0057 -0.04,2.8358 2.181,5.1453 5.006,5.1882 2.234,0.032 4.193,-2.163 4.913,-4.1502 6.821,5.7253 7.109,13.2267 10.138,22.2067 2.584,7.6677 5.241,15.55 9.624,21.0386 -12.257,-2.0799 -18.921,0.4868 -25.895,0.099 -8.749,-0.5118 -13.702,-0.7047 -18.969,-4.7023 0.76,-0.9754 2.48,-2.9979 2.493,-4.3153 0.06,-3.3316 -2.584,-6.0678 -5.916,-6.1184 -3.293,-0.076 -6.042,2.5715 -6.106,5.8904 -0.05,2.9008 2.015,5.2443 4.751,5.865 13.883,7.0304 13.785,5.7411 23.59,6.3272 6.933,0.3879 13.564,-2.1788 25.755,-0.099 -2.352,4.3002 -2.237,8.914 0.487,10.7628 1.964,1.3454 4.812,0.8501 7.42,-0.9657 3.12,3.3592 7.899,7.6462 11.34,11.9133 6.339,7.8823 44.912,31.4581 48.829,30.5519 1.849,-0.7429 -1.997,-6.2068 -7.973,-12.9913 1.692,0.8584 3.244,1.6094 4.565,2.2367 1.939,0.7924 47.425,18.8597 73.424,8.6581 21.699,-0.4457 33.395,-13.0903 33.824,-36.6298 0.247,-15.0051 -3.888,-33.8318 -12.686,-57.5446 z m -163.489,-117.9285 c 0.495,-27.4765 6.909,-41.2932 18.736,-41.095 12.315,1.2793 19.537,29.4903 23.011,43.0511 0.586,2.278 1.106,4.3415 1.585,6.0747 1.89,6.7846 11.225,15.3189 26.305,28.6981 13.841,12.2814 32.799,29.1024 32.659,37.6697 l 3.921,0.066 -3.929,0.066 c -0.256,5.7528 -10.457,18.9504 -21.633,32.4782 -5.216,4.6716 -15.533,17.1924 -25.297,30.5303 -8.98,11.3488 -16.475,22.0621 -19.859,30.8192 -0.759,1.5187 -1.403,2.9053 -1.89,4.1599 -1.51,-0.8831 -2.979,-1.7415 -4.399,-2.5669 -19.759,-75.2074 -29.853,-133.9654 -29.21,-169.9514 z m 141.526,204.2537 h -0.768 l -0.717,0.3054 c -22.913,9.5907 -67.747,-8.2207 -67.962,-8.2867 -12.512,-6.0252 -26.552,-14.1551 -30.736,-18.2324 0.05,-0.5778 0.09,-1.172 0.06,-1.8158 0.165,-1.6012 0.693,-3.4418 1.477,-5.4227 3.541,-6.6607 10.317,-16.9777 17.737,-27.2701 5.613,-7.1312 11.811,-14.5182 17.746,-21.5751 2.773,-3.3015 5.373,-6.3966 7.833,-9.3762 15.929,-12.5043 32.469,-17.2006 46.6,-13.2388 10.795,3.029 19.511,10.9938 23.333,21.2944 3.103,8.394 12.554,33.8731 12.19,54.6806 -0.346,19.619 -8.864,28.8053 -26.791,28.9374 z"
229 |              id="path6027"
230 |              style="fill:#000000;fill-opacity:1;stroke-width:8.25367451" />
231 |           <path
232 |              style="stroke-width:8.25367451"
233 |              inkscape:connector-curvature="0"
234 |              d="m -16771.959,2575.4801 c 7.19,3.9287 18.621,1.6342 19.603,-7.5108 0.982,-8.1629 -4.903,-16.9861 -8.493,-23.2011 -2.286,-3.9205 3.425,-23.8531 -8.906,-22.4665 -12.331,1.4031 -10.045,27.6993 -9.393,35.2267 0.331,6.5369 0.983,14.6998 7.189,17.9517 z"
235 |              id="path6029" />
236 |           <path
237 |              style="stroke-width:8.25367451"
238 |              inkscape:connector-curvature="0"
239 |              d="m -16732.423,2638.5547 c -6.868,17.6463 -15.36,35.2762 -20.915,53.5828 -0.974,3.2685 12.1,-0.6437 17.646,-7.8492 5.233,-6.1903 41.425,-44.2149 37.917,-57.165 -3.524,-12.95 -15.351,-16.334 -24.843,-8.8231 -5.885,4.5808 -7.189,13.3957 -9.805,20.2545 z"
240 |              id="path6031" />
241 |           <path
242 |              style="stroke-width:8.25367451"
243 |              inkscape:connector-curvature="0"
244 |              d="m -16753.99,2654.2284 c 1.304,-9.8053 0.652,-20.2545 0.33,-29.7297 0,-9.8054 -0.652,-18.9587 -4.573,-28.0955 -3.928,-9.1533 -17.324,-5.8849 -17.324,-5.8849 0,0 11.44,74.1593 12.413,90.1796 6.868,-2.5999 8.502,-20.2545 9.154,-26.4695 z"
245 |              id="path6033" />
246 |           <path
247 |              style="stroke-width:8.25367451"
248 |              inkscape:connector-curvature="0"
249 |              d="m -16746.372,2593.5639 c 8.493,-3.9205 8.658,-12.1742 -1.312,-14.3779 -9.962,-2.2037 -10.128,19.2806 1.312,14.3779 z"
250 |              id="path6035" />
251 |           <path
252 |              style="stroke-width:8.25367451"
253 |              inkscape:connector-curvature="0"
254 |              d="m -16729.023,2612.762 c 6.908,0 4.465,-6.3306 4.465,-6.3306 l -2.162,-2.0139 c -6.05,-3.7307 -11.794,8.6498 -2.303,8.3445 z"
255 |              id="path6037" />
256 |           <path
257 |              style="stroke-width:8.25367451"
258 |              inkscape:connector-curvature="0"
259 |              d="m -16730.921,2594.3562 c -4.391,-2.7154 -8.576,6.2728 -1.676,6.0582 5.019,0 3.244,-4.5972 3.244,-4.5972 z"
260 |              id="path6039" />
261 |           <path
262 |              style="stroke-width:8.25367451"
263 |              inkscape:connector-curvature="0"
264 |              d="m -16717.385,2612.7702 c 5.018,0 3.243,-4.5973 3.243,-4.5973 l -1.56,-1.4774 c -4.398,-2.7072 -8.575,6.281 -1.683,6.0747 z"
265 |              id="path6041" />
266 |           <path
267 |              style="stroke-width:8.25367451"
268 |              inkscape:connector-curvature="0"
269 |              d="m -16734.677,2585.9375 c 3.062,0 1.981,-2.8062 1.981,-2.8062 l -0.957,-0.8914 c -2.674,-1.659 -5.233,3.8379 -1.024,3.6976 z"
270 |              id="path6043" />
271 |           <path
272 |              style="stroke-width:8.25367451"
273 |              inkscape:connector-curvature="0"
274 |              d="m -16730.418,2589.9571 c 3.062,0 1.973,-2.8063 1.973,-2.8063 l -0.949,-0.8997 c -2.691,-1.6589 -5.233,3.8297 -1.024,3.706 z"
275 |              id="path6045" />
276 |           <path
277 |              style="stroke-width:8.25367451"
278 |              inkscape:connector-curvature="0"
279 |              d="m -16726.027,2593.9766 c 3.063,0 1.973,-2.8145 1.973,-2.8145 l -0.949,-0.8914 c -2.674,-1.659 -5.233,3.8214 -1.024,3.7059 z"
280 |              id="path6047" />
281 |           <path
282 |              style="stroke-width:8.25367451"
283 |              inkscape:connector-curvature="0"
284 |              d="m -16720.315,2598.3015 c 3.062,0 1.981,-2.8062 1.981,-2.8062 l -0.958,-0.8914 c -2.682,-1.6425 -5.233,3.8462 -1.023,3.6976 z"
285 |              id="path6049" />
286 |           <path
287 |              style="stroke-width:8.25367451"
288 |              inkscape:connector-curvature="0"
289 |              d="m -16725.433,2602.3293 c 3.062,0 1.981,-2.8145 1.981,-2.8145 l -0.957,-0.8914 c -2.683,-1.659 -5.233,3.8215 -1.024,3.7059 z"
290 |              id="path6051" />
291 |           <path
292 |              style="stroke-width:8.25367451"
293 |              inkscape:connector-curvature="0"
294 |              d="m -16736.872,2591.9462 c 3.062,0 1.973,-2.798 1.973,-2.798 l -0.958,-0.8914 c -2.674,-1.6508 -5.216,3.8297 -1.015,3.6894 z"
295 |              id="path6053" />
296 |           <path
297 |              style="stroke-width:8.25367451"
298 |              inkscape:connector-curvature="0"
299 |              d="m -16742.468,2602.5439 c -3.269,-0.9905 -3.995,16.7385 -4.325,21.3192 -0.652,5.2246 -0.982,11.4314 -0.652,16.6642 6.537,-4.2506 8.163,-16.6642 10.788,-23.5312 1.295,-3.9288 0.06,-13.4865 -5.811,-14.4522 z"
300 |              id="path6055" />
301 |           <path
302 |              style="stroke-width:8.25367451"
303 |              inkscape:connector-curvature="0"
304 |              d="m -16642.896,2686.9047 c 1.313,-4.5808 -3.516,-16.788 -14.7,-20.9148 -11.183,-4.1104 -16.664,-1.9479 -24.513,1.6425 -15.666,6.5286 -30.39,22.2106 -40.187,35.6063 -4.573,6.2068 -7.833,13.3957 -12.413,19.9244 14.377,-6.5287 30.398,-4.5808 45.419,-8.8149 14.37,-3.9123 41.5,-10.7628 46.394,-27.4435 z"
305 |              id="path6057" />
306 |           <path
307 |              style="stroke-width:8.25367451"
308 |              inkscape:connector-curvature="0"
309 |              d="m -16635.055,2688.217 c -5.992,-4.4487 -11.431,30.7285 -19.602,37.579 -8.163,6.8671 -18.67,8.2124 -25.529,12.7849 -4.903,3.6069 -8.774,8.4518 -2.583,13.3628 7.189,5.5629 14.056,4.5642 22.871,4.2423 7.841,-0.3218 18.768,-3.45 25.487,-5.8683 12.785,-5.7611 7.915,-55.7453 -0.644,-62.1007 z"
310 |              id="path6059" />
311 |           <path
312 |              style="stroke-width:8.25367451"
313 |              inkscape:connector-curvature="0"
314 |              d="m -16657.868,2715.1735 c -2.418,-3.9947 -35.004,6.0417 -49.373,9.3184 -4.911,0.9822 -28.756,1.5187 -25.488,8.6912 2.608,5.8931 21.889,13.5277 27.774,12.5456 7.18,-1.3124 49.489,-26.5521 47.087,-30.5552 z"
315 |              id="path6061" />
316 |           <path
317 |              style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.48915815;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
318 |              d="m -16764.712,2736.1111 c 0,0 -0.173,7.6946 -0.815,11.4752 -0.705,4.1533 -0.958,12.1041 -3.316,12.1955"
319 |              id="path6063"
320 |              inkscape:connector-curvature="0"
321 |              sodipodi:nodetypes="cac" />
322 |           <path
323 |              style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.48915815;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
324 |              d="m -16755.55,2743.6046 c -13.433,-0.6599 -16.739,-0.4504 -16.602,6.6417 0.06,3.1027 -1.113,6.4396 -1.113,6.4396"
325 |              id="path6065"
326 |              inkscape:connector-curvature="0"
327 |              sodipodi:nodetypes="csc" />
328 |           <path
329 |              sodipodi:nodetypes="cac"
330 |              inkscape:connector-curvature="0"
331 |              id="path6067"
332 |              d="m -16749.559,2746.4269 c 0,0 2.513,6.9749 3.377,10.572 1.883,7.8394 5.984,22.7081 3.908,23.869"
333 |              style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.43702245;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
334 |              inkscape:transform-center-x="6.0507461"
335 |              inkscape:transform-center-y="4.1553339" />
336 |           <path
337 |              inkscape:transform-center-y="2.1231972"
338 |              inkscape:transform-center-x="6.9332847"
339 |              style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.43702245;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
340 |              d="m -16749.918,2743.3152 c 0,0 0.214,7.4009 0.08,11.0978 -0.227,6.1929 0.835,18.0404 -1.492,18.5313"
341 |              id="path6069"
342 |              inkscape:connector-curvature="0"
343 |              sodipodi:nodetypes="cac" />
344 |         </g>
345 |       </g>
346 |     </g>
347 |   </g>
348 | </svg>
349 | 


--------------------------------------------------------------------------------
/web/media/insect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sharkdp/insect/d19e4f15f788a4c1bd277e1ef3bd5a5e3228bdd3/web/media/insect.png


--------------------------------------------------------------------------------
/web/media/insect.svg:
--------------------------------------------------------------------------------
  1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 | <!-- Created with Inkscape (http://www.inkscape.org/) -->
  3 | 
  4 | <svg
  5 |    xmlns:dc="http://purl.org/dc/elements/1.1/"
  6 |    xmlns:cc="http://creativecommons.org/ns#"
  7 |    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  8 |    xmlns:svg="http://www.w3.org/2000/svg"
  9 |    xmlns="http://www.w3.org/2000/svg"
 10 |    xmlns:xlink="http://www.w3.org/1999/xlink"
 11 |    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 12 |    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 13 |    width="192"
 14 |    height="192"
 15 |    viewBox="0 0 57.150001 57.149997"
 16 |    version="1.1"
 17 |    id="svg8"
 18 |    inkscape:version="0.92.1 r"
 19 |    sodipodi:docname="insect-favicon.svg"
 20 |    inkscape:export-filename="/home/shark/Dropbox/Informatik/purescript/insect/media/insect-196x196.png"
 21 |    inkscape:export-xdpi="98"
 22 |    inkscape:export-ydpi="98">
 23 |   <defs
 24 |      id="defs2">
 25 |     <linearGradient
 26 |        inkscape:collect="always"
 27 |        id="linearGradient6015">
 28 |       <stop
 29 |          style="stop-color:#f72d4b;stop-opacity:1"
 30 |          offset="0"
 31 |          id="stop6009" />
 32 |       <stop
 33 |          id="stop6011"
 34 |          offset="0.31170031"
 35 |          style="stop-color:#b1076f;stop-opacity:1" />
 36 |       <stop
 37 |          style="stop-color:#078af4;stop-opacity:1"
 38 |          offset="1"
 39 |          id="stop6013" />
 40 |     </linearGradient>
 41 |     <linearGradient
 42 |        inkscape:collect="always"
 43 |        xlink:href="#linearGradient6015"
 44 |        id="linearGradient4733"
 45 |        gradientUnits="userSpaceOnUse"
 46 |        gradientTransform="matrix(0.09407842,0,0,0.09407842,-16454.439,2288.3277)"
 47 |        x1="-2996.7202"
 48 |        y1="4654.9209"
 49 |        x2="-1559.0183"
 50 |        y2="4200.6133" />
 51 |     <linearGradient
 52 |        inkscape:collect="always"
 53 |        xlink:href="#linearGradient6015"
 54 |        id="linearGradient4735"
 55 |        gradientUnits="userSpaceOnUse"
 56 |        gradientTransform="matrix(0.02489158,0,0,0.02489158,-16447.602,2300.0458)"
 57 |        x1="-12403.771"
 58 |        y1="16503.461"
 59 |        x2="-12550.984"
 60 |        y2="9437.1182" />
 61 |   </defs>
 62 |   <sodipodi:namedview
 63 |      id="base"
 64 |      pagecolor="#ecf0f1"
 65 |      bordercolor="#666666"
 66 |      borderopacity="1.0"
 67 |      inkscape:pageopacity="0"
 68 |      inkscape:pageshadow="2"
 69 |      inkscape:zoom="1.28"
 70 |      inkscape:cx="123.35711"
 71 |      inkscape:cy="32.239565"
 72 |      inkscape:document-units="mm"
 73 |      inkscape:current-layer="g6297"
 74 |      showgrid="false"
 75 |      inkscape:window-width="1920"
 76 |      inkscape:window-height="1176"
 77 |      inkscape:window-x="1920"
 78 |      inkscape:window-y="0"
 79 |      inkscape:window-maximized="0"
 80 |      showborder="true"
 81 |      inkscape:snap-global="false"
 82 |      showguides="false"
 83 |      inkscape:guide-bbox="true"
 84 |      fit-margin-top="5"
 85 |      fit-margin-left="5"
 86 |      fit-margin-right="5"
 87 |      fit-margin-bottom="5"
 88 |      inkscape:pagecheckerboard="false"
 89 |      units="px">
 90 |     <sodipodi:guide
 91 |        position="-39.951962,3021.5609"
 92 |        orientation="0,1"
 93 |        id="guide4507"
 94 |        inkscape:locked="false" />
 95 |     <sodipodi:guide
 96 |        position="-137.11484,3100.2277"
 97 |        orientation="0,1"
 98 |        id="guide4509"
 99 |        inkscape:locked="false" />
100 |   </sodipodi:namedview>
101 |   <metadata
102 |      id="metadata5">
103 |     <rdf:RDF>
104 |       <cc:Work
105 |          rdf:about="">
106 |         <dc:format>image/svg+xml</dc:format>
107 |         <dc:type
108 |            rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
109 |         <dc:title></dc:title>
110 |       </cc:Work>
111 |     </rdf:RDF>
112 |   </metadata>
113 |   <g
114 |      inkscape:label="logo"
115 |      inkscape:groupmode="layer"
116 |      id="layer1"
117 |      transform="translate(-212.55597,-2905.2003)">
118 |     <flowRoot
119 |        xml:space="preserve"
120 |        id="flowRoot4625"
121 |        style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:25px;font-family:'Bitstream Vera Sans';-inkscape-font-specification:'Bitstream Vera Sans';letter-spacing:0px;word-spacing:0px;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
122 |        transform="scale(0.26458333)"><flowRegion
123 |          id="flowRegion4627"><rect
124 |            id="rect4629"
125 |            width="989.94946"
126 |            height="56.568542"
127 |            x="1882.9243"
128 |            y="827.55511" /></flowRegion><flowPara
129 |          id="flowPara4631" /></flowRoot>    <g
130 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
131 |        id="g4839" />
132 |     <g
133 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
134 |        id="g4841" />
135 |     <g
136 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
137 |        id="g4843" />
138 |     <g
139 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
140 |        id="g4845" />
141 |     <g
142 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
143 |        id="g4847" />
144 |     <g
145 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
146 |        id="g4849" />
147 |     <g
148 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
149 |        id="g4851" />
150 |     <g
151 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
152 |        id="g4853" />
153 |     <g
154 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
155 |        id="g4855" />
156 |     <g
157 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
158 |        id="g4857" />
159 |     <g
160 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
161 |        id="g4859" />
162 |     <g
163 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
164 |        id="g4861" />
165 |     <g
166 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
167 |        id="g4863" />
168 |     <g
169 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
170 |        id="g4865" />
171 |     <g
172 |        transform="matrix(0.26458333,0,0,0.26458333,-1671.482,5468.0976)"
173 |        id="g4867" />
174 |     <g
175 |        id="g6297"
176 |        transform="matrix(0.27096735,0,0,0.27096735,156.04521,2132.9182)">
177 |       <g
178 |          transform="matrix(-0.42192027,0.58342223,0.58342223,0.42192027,-8294.8792,11588.273)"
179 |          id="g6071">
180 |         <path
181 |            style="opacity:1;fill:url(#linearGradient4733);fill-opacity:1;stroke:#ffffff;stroke-width:3.61613226;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
182 |            d="m -16670.852,2763.4152 c -16.3,-1.4311 -37.895,-7.4178 -51.413,-14.2525 -8.155,-4.124 -17.496,-9.5735 -22.327,-13.0253 l -3.088,-2.206 0.201,-2.4221 c 0.287,-3.4573 2.792,-8.2186 10.199,-19.3823 8.26,-12.4489 9.631,-14.2576 23.313,-30.7473 12.318,-14.8473 13.878,-16.3687 21.395,-20.8632 17.59,-10.518 35.173,-11.4947 48.418,-2.6897 5.093,3.386 9.665,8.6002 12.292,14.0181 4.284,8.836 10.321,29.0433 12.71,42.5424 0.86,4.8613 1.038,7.4374 1.025,14.9163 -0.02,9.8641 -0.627,13.4656 -3.163,18.667 -1.621,3.3259 -5.486,7.3489 -8.716,9.0702 -3.411,1.818 -8.857,3.2261 -12.479,3.2261 -1.662,0 -3.878,0.3786 -5.465,0.9335 -5.933,2.0741 -14.769,2.9286 -22.902,2.2148 z"
183 |            id="path6023"
184 |            inkscape:connector-curvature="0" />
185 |         <path
186 |            style="opacity:1;fill:url(#linearGradient4735);fill-opacity:1;stroke:#ffffff;stroke-width:1.7601006;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
187 |            d="m -16763.593,2726.1517 c -1.583,-1.8075 -4.938,-32.5076 -6.528,-39.2769 -11.029,-46.9448 -17.561,-83.5036 -19.662,-114.6954 -0.358,-5.3012 -1.602,-23.2314 -1.245,-27.1416 1.123,-12.3057 3.317,-19.8129 6.809,-25.2103 3.629,-5.6098 14.242,-10.2452 18.969,-7.9179 4.293,2.1131 10.662,9.8218 14.369,19.1951 2.47,6.2435 4.12,11.9259 7.307,24.0974 1.98,7.561 4.723,9.8665 6.043,12.0396 15.299,19.5183 29.721,25.944 47.625,46.0566 5.421,10.559 8.647,19.0396 2.348,28.1929 -4.611,6.9892 -15.108,19.0656 -20.092,24.1956 -13.159,25.9004 -11.931,13.2756 -19.703,23.5738 -2.284,3.0252 -4.712,6.4126 -6.158,8.2882 -7.137,9.2601 -10.744,18.4583 -13.31,24.2483 -1.641,3.7044 -5.093,11.1342 -5.228,11.2835 -8.714,-5.5279 -7.808,-2.464 -11.544,-6.9289 z"
188 |            id="path6025"
189 |            inkscape:connector-curvature="0"
190 |            sodipodi:nodetypes="ccscccssccccccccc" />
191 |         <path
192 |            style="fill:#000000;fill-opacity:1;stroke-width:8.25367451"
193 |            id="path6027"
194 |            d="m -16624.127,2672.9725 c -4.688,-12.6529 -15.368,-22.4252 -28.566,-26.1311 -10.92,-3.0622 -22.879,-1.8406 -34.946,3.384 6.314,-8.6416 9.913,-15.2611 10.144,-20.2958 l 0.01,-0.3467 c 0.207,-12.1824 -17.836,-28.1863 -35.284,-43.6784 -10.582,-9.3927 -22.574,-20.0317 -23.944,-24.9344 -0.479,-1.692 -0.991,-3.6894 -1.552,-5.9261 -4.506,-17.6051 -12.05,-47.046 -30.126,-48.9113 -17.39,-0.3136 -26.494,16.1029 -27.072,48.771 -0.635,35.5321 8.906,92.4246 27.576,164.8671 -0.702,-0.3714 -1.362,-0.7098 -1.932,-1.0151 0.933,-3.2933 0.396,-6.3636 -1.7,-7.775 -2.278,-1.5517 -5.736,-0.652 -8.633,1.9066 -4.416,-5.0678 -7.132,-13.0573 -9.764,-20.8241 -3.5,-10.4161 -4.162,-20.7115 -14.506,-26.0716 -0.86,-0.7304 -1.923,-1.2353 -3.147,-1.2461 -2.815,-0.054 -5.135,2.1806 -5.178,5.0057 -0.04,2.8358 2.181,5.1453 5.006,5.1882 2.234,0.032 4.193,-2.163 4.913,-4.1502 6.821,5.7253 7.109,13.2267 10.138,22.2067 2.584,7.6677 5.241,15.55 9.624,21.0386 -12.257,-2.0799 -18.921,0.4868 -25.895,0.099 -8.749,-0.5118 -13.702,-0.7047 -18.969,-4.7023 0.76,-0.9754 2.48,-2.9979 2.493,-4.3153 0.06,-3.3316 -2.584,-6.0678 -5.916,-6.1184 -3.293,-0.076 -6.042,2.5715 -6.106,5.8904 -0.05,2.9008 2.015,5.2443 4.751,5.865 13.883,7.0304 13.785,5.7411 23.59,6.3272 6.933,0.3879 13.564,-2.1788 25.755,-0.099 -2.352,4.3002 -2.237,8.914 0.487,10.7628 1.964,1.3454 4.812,0.8501 7.42,-0.9657 3.12,3.3592 7.899,7.6462 11.34,11.9133 6.339,7.8823 44.912,31.4581 48.829,30.5519 1.849,-0.7429 -1.997,-6.2068 -7.973,-12.9913 1.692,0.8584 3.244,1.6094 4.565,2.2367 1.939,0.7924 47.425,18.8597 73.424,8.6581 21.699,-0.4457 33.395,-13.0903 33.824,-36.6298 0.247,-15.0051 -3.888,-33.8318 -12.686,-57.5446 z m -163.489,-117.9285 c 0.495,-27.4765 6.909,-41.2932 18.736,-41.095 12.315,1.2793 19.537,29.4903 23.011,43.0511 0.586,2.278 1.106,4.3415 1.585,6.0747 1.89,6.7846 11.225,15.3189 26.305,28.6981 13.841,12.2814 32.799,29.1024 32.659,37.6697 l 3.921,0.066 -3.929,0.066 c -0.256,5.7528 -10.457,18.9504 -21.633,32.4782 -5.216,4.6716 -15.533,17.1924 -25.297,30.5303 -8.98,11.3488 -16.475,22.0621 -19.859,30.8192 -0.759,1.5187 -1.403,2.9053 -1.89,4.1599 -1.51,-0.8831 -2.979,-1.7415 -4.399,-2.5669 -19.759,-75.2074 -29.853,-133.9654 -29.21,-169.9514 z m 141.526,204.2537 h -0.768 l -0.717,0.3054 c -22.913,9.5907 -67.747,-8.2207 -67.962,-8.2867 -12.512,-6.0252 -26.552,-14.1551 -30.736,-18.2324 0.05,-0.5778 0.09,-1.172 0.06,-1.8158 0.165,-1.6012 0.693,-3.4418 1.477,-5.4227 3.541,-6.6607 10.317,-16.9777 17.737,-27.2701 5.613,-7.1312 11.811,-14.5182 17.746,-21.5751 2.773,-3.3015 5.373,-6.3966 7.833,-9.3762 15.929,-12.5043 32.469,-17.2006 46.6,-13.2388 10.795,3.029 19.511,10.9938 23.333,21.2944 3.103,8.394 12.554,33.8731 12.19,54.6806 -0.346,19.619 -8.864,28.8053 -26.791,28.9374 z"
195 |            inkscape:connector-curvature="0"
196 |            sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" />
197 |         <path
198 |            id="path6029"
199 |            d="m -16771.959,2575.4801 c 7.19,3.9287 18.621,1.6342 19.603,-7.5108 0.982,-8.1629 -4.903,-16.9861 -8.493,-23.2011 -2.286,-3.9205 3.425,-23.8531 -8.906,-22.4665 -12.331,1.4031 -10.045,27.6993 -9.393,35.2267 0.331,6.5369 0.983,14.6998 7.189,17.9517 z"
200 |            inkscape:connector-curvature="0"
201 |            style="stroke-width:8.25367451" />
202 |         <path
203 |            id="path6031"
204 |            d="m -16732.423,2638.5547 c -6.868,17.6463 -15.36,35.2762 -20.915,53.5828 -0.974,3.2685 12.1,-0.6437 17.646,-7.8492 5.233,-6.1903 41.425,-44.2149 37.917,-57.165 -3.524,-12.95 -15.351,-16.334 -24.843,-8.8231 -5.885,4.5808 -7.189,13.3957 -9.805,20.2545 z"
205 |            inkscape:connector-curvature="0"
206 |            style="stroke-width:8.25367451" />
207 |         <path
208 |            id="path6033"
209 |            d="m -16753.99,2654.2284 c 1.304,-9.8053 0.652,-20.2545 0.33,-29.7297 0,-9.8054 -0.652,-18.9587 -4.573,-28.0955 -3.928,-9.1533 -17.324,-5.8849 -17.324,-5.8849 0,0 11.44,74.1593 12.413,90.1796 6.868,-2.5999 8.502,-20.2545 9.154,-26.4695 z"
210 |            inkscape:connector-curvature="0"
211 |            style="stroke-width:8.25367451" />
212 |         <path
213 |            id="path6035"
214 |            d="m -16746.372,2593.5639 c 8.493,-3.9205 8.658,-12.1742 -1.312,-14.3779 -9.962,-2.2037 -10.128,19.2806 1.312,14.3779 z"
215 |            inkscape:connector-curvature="0"
216 |            style="stroke-width:8.25367451" />
217 |         <path
218 |            id="path6037"
219 |            d="m -16729.023,2612.762 c 6.908,0 4.465,-6.3306 4.465,-6.3306 l -2.162,-2.0139 c -6.05,-3.7307 -11.794,8.6498 -2.303,8.3445 z"
220 |            inkscape:connector-curvature="0"
221 |            style="stroke-width:8.25367451" />
222 |         <path
223 |            id="path6039"
224 |            d="m -16730.921,2594.3562 c -4.391,-2.7154 -8.576,6.2728 -1.676,6.0582 5.019,0 3.244,-4.5972 3.244,-4.5972 z"
225 |            inkscape:connector-curvature="0"
226 |            style="stroke-width:8.25367451" />
227 |         <path
228 |            id="path6041"
229 |            d="m -16717.385,2612.7702 c 5.018,0 3.243,-4.5973 3.243,-4.5973 l -1.56,-1.4774 c -4.398,-2.7072 -8.575,6.281 -1.683,6.0747 z"
230 |            inkscape:connector-curvature="0"
231 |            style="stroke-width:8.25367451" />
232 |         <path
233 |            id="path6043"
234 |            d="m -16734.677,2585.9375 c 3.062,0 1.981,-2.8062 1.981,-2.8062 l -0.957,-0.8914 c -2.674,-1.659 -5.233,3.8379 -1.024,3.6976 z"
235 |            inkscape:connector-curvature="0"
236 |            style="stroke-width:8.25367451" />
237 |         <path
238 |            id="path6045"
239 |            d="m -16730.418,2589.9571 c 3.062,0 1.973,-2.8063 1.973,-2.8063 l -0.949,-0.8997 c -2.691,-1.6589 -5.233,3.8297 -1.024,3.706 z"
240 |            inkscape:connector-curvature="0"
241 |            style="stroke-width:8.25367451" />
242 |         <path
243 |            id="path6047"
244 |            d="m -16726.027,2593.9766 c 3.063,0 1.973,-2.8145 1.973,-2.8145 l -0.949,-0.8914 c -2.674,-1.659 -5.233,3.8214 -1.024,3.7059 z"
245 |            inkscape:connector-curvature="0"
246 |            style="stroke-width:8.25367451" />
247 |         <path
248 |            id="path6049"
249 |            d="m -16720.315,2598.3015 c 3.062,0 1.981,-2.8062 1.981,-2.8062 l -0.958,-0.8914 c -2.682,-1.6425 -5.233,3.8462 -1.023,3.6976 z"
250 |            inkscape:connector-curvature="0"
251 |            style="stroke-width:8.25367451" />
252 |         <path
253 |            id="path6051"
254 |            d="m -16725.433,2602.3293 c 3.062,0 1.981,-2.8145 1.981,-2.8145 l -0.957,-0.8914 c -2.683,-1.659 -5.233,3.8215 -1.024,3.7059 z"
255 |            inkscape:connector-curvature="0"
256 |            style="stroke-width:8.25367451" />
257 |         <path
258 |            id="path6053"
259 |            d="m -16736.872,2591.9462 c 3.062,0 1.973,-2.798 1.973,-2.798 l -0.958,-0.8914 c -2.674,-1.6508 -5.216,3.8297 -1.015,3.6894 z"
260 |            inkscape:connector-curvature="0"
261 |            style="stroke-width:8.25367451" />
262 |         <path
263 |            id="path6055"
264 |            d="m -16742.468,2602.5439 c -3.269,-0.9905 -3.995,16.7385 -4.325,21.3192 -0.652,5.2246 -0.982,11.4314 -0.652,16.6642 6.537,-4.2506 8.163,-16.6642 10.788,-23.5312 1.295,-3.9288 0.06,-13.4865 -5.811,-14.4522 z"
265 |            inkscape:connector-curvature="0"
266 |            style="stroke-width:8.25367451" />
267 |         <path
268 |            id="path6057"
269 |            d="m -16642.896,2686.9047 c 1.313,-4.5808 -3.516,-16.788 -14.7,-20.9148 -11.183,-4.1104 -16.664,-1.9479 -24.513,1.6425 -15.666,6.5286 -30.39,22.2106 -40.187,35.6063 -4.573,6.2068 -7.833,13.3957 -12.413,19.9244 14.377,-6.5287 30.398,-4.5808 45.419,-8.8149 14.37,-3.9123 41.5,-10.7628 46.394,-27.4435 z"
270 |            inkscape:connector-curvature="0"
271 |            style="stroke-width:8.25367451" />
272 |         <path
273 |            id="path6059"
274 |            d="m -16635.055,2688.217 c -5.992,-4.4487 -11.431,30.7285 -19.602,37.579 -8.163,6.8671 -18.67,8.2124 -25.529,12.7849 -4.903,3.6069 -8.774,8.4518 -2.583,13.3628 7.189,5.5629 14.056,4.5642 22.871,4.2423 7.841,-0.3218 18.768,-3.45 25.487,-5.8683 12.785,-5.7611 7.915,-55.7453 -0.644,-62.1007 z"
275 |            inkscape:connector-curvature="0"
276 |            style="stroke-width:8.25367451" />
277 |         <path
278 |            id="path6061"
279 |            d="m -16657.868,2715.1735 c -2.418,-3.9947 -35.004,6.0417 -49.373,9.3184 -4.911,0.9822 -28.756,1.5187 -25.488,8.6912 2.608,5.8931 21.889,13.5277 27.774,12.5456 7.18,-1.3124 49.489,-26.5521 47.087,-30.5552 z"
280 |            inkscape:connector-curvature="0"
281 |            style="stroke-width:8.25367451" />
282 |         <path
283 |            sodipodi:nodetypes="cac"
284 |            inkscape:connector-curvature="0"
285 |            id="path6063"
286 |            d="m -16764.712,2736.1111 c 0,0 -0.173,7.6946 -0.815,11.4752 -0.705,4.1533 -0.958,12.1041 -3.316,12.1955"
287 |            style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.48915815;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
288 |         <path
289 |            sodipodi:nodetypes="csc"
290 |            inkscape:connector-curvature="0"
291 |            id="path6065"
292 |            d="m -16755.55,2743.6046 c -13.433,-0.6599 -16.739,-0.4504 -16.602,6.6417 0.06,3.1027 -1.113,6.4396 -1.113,6.4396"
293 |            style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.48915815;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
294 |         <path
295 |            inkscape:transform-center-y="4.1553339"
296 |            inkscape:transform-center-x="6.0507461"
297 |            style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.43702245;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
298 |            d="m -16749.559,2746.4269 c 0,0 2.513,6.9749 3.377,10.572 1.883,7.8394 5.984,22.7081 3.908,23.869"
299 |            id="path6067"
300 |            inkscape:connector-curvature="0"
301 |            sodipodi:nodetypes="cac" />
302 |         <path
303 |            sodipodi:nodetypes="cac"
304 |            inkscape:connector-curvature="0"
305 |            id="path6069"
306 |            d="m -16749.918,2743.3152 c 0,0 0.214,7.4009 0.08,11.0978 -0.227,6.1929 0.835,18.0404 -1.492,18.5313"
307 |            style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.43702245;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
308 |            inkscape:transform-center-x="6.9332847"
309 |            inkscape:transform-center-y="2.1231972" />
310 |       </g>
311 |     </g>
312 |   </g>
313 | </svg>
314 | 


--------------------------------------------------------------------------------
/web/opensearch.xml:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">
 3 |   <ShortName>insect.sh</ShortName>
 4 |   <Description>High precision scientific calculator with full support for physical units</Description>
 5 |   <Tags>calculator scientific math physics unit conversion quantity SI imperial precision</Tags>
 6 |   <Url type="text/html" method="get" template="https://insect.sh/?q={searchTerms}"/>
 7 |   <InputEncoding>UTF-8</InputEncoding>
 8 |   <Url type="application/opensearchdescription+xml" rel="self" template="https://insect.sh/opensearch.xml"/>
 9 | </OpenSearchDescription>
10 | 


--------------------------------------------------------------------------------