Chemical calculator: Difference between revisions
Content added Content deleted
(JavaScript added) |
|||
Line 2,190: | Line 2,190: | ||
C₂₇H₄₆O: 387 |
C₂₇H₄₆O: 387 |
||
Uue: 315 |
Uue: 315 |
||
</pre> |
|||
=={{header|jq}}== |
|||
{{works with|jq}} |
|||
'''Works with gojq, the Go implementation of jq''' |
|||
jq is well-suited to "Parsing Expression Grammars" (PEGs) so |
|||
this entry illustrates how to implement the chemical calculator using a PEG approach. |
|||
In the remainder of this entry we focus on the highlights. The complete program is on the |
|||
subpage at [[Chemical calculator/jq]]. |
|||
To understand the PEG grammar presented below, here is a table showing |
|||
the correspondence between the main PEG operators (on the left) and jq constructs (on the right), |
|||
the first two of which (`|` and `//`) |
|||
are part of the jq language, and the others of which are defined as jq functions |
|||
on the subpage. |
|||
<pre> |
|||
Sequence: e1 e2 e1 | e2 |
|||
Ordered choice: e1 / e2 e1 // e2 |
|||
Zero-or-more: e* star(E) |
|||
One-or-more: e+ plus(E) |
|||
Optional: e? optional(E) |
|||
And-predicate: &e amp(E) |
|||
Not-predicate: !e neg(E) |
|||
</pre> |
|||
'''The PEG grammar''' |
|||
<lang jq>def Element: |
|||
parse("(?<e>^[A-Z][a-z]*)"); # greedy |
|||
def Number: parseNumber("^[0-9]+"); # greedy |
|||
def EN: Element | optional(Number); |
|||
def Parenthesized: |
|||
consume("[(]") |
|||
| box( (plus(EN) | optional(Parenthesized)) // (Parenthesized | plus(EN)) ) |
|||
| consume("[)]") |
|||
| Number; |
|||
def Formula: |
|||
(plus(EN) | Parenthesized | Formula) |
|||
// (plus(EN) | optional(Parenthesized)) |
|||
// (Parenthesized | Formula) ;</lang> |
|||
'''Evaluation of the parsed expression''' |
|||
This is accomplished using the `eval` function defined on the subpage. |
|||
'''The task expressed in terms of assertions''' |
|||
<lang># A "debug" statement has been retained so that the parsed chemical formula can be seen. |
|||
def molar_mass(formula): |
|||
{remainder: formula} | Formula | .result | debug | eval; |
|||
def assert(a; b): |
|||
if (a - b) > 1e-3 then "\(a) != \(b)" else empty end; |
|||
def task: |
|||
assert( 1.008; molar_mass("H")), # hydrogen |
|||
assert( 2.016; molar_mass("H2")), # hydrogen gas |
|||
assert( 18.015; molar_mass("H2O")), # water |
|||
assert( 34.014; molar_mass("H2O2")), # hydrogen peroxide |
|||
assert( 34.014; molar_mass("(HO)2")), # hydrogen peroxide |
|||
assert( 142.036; molar_mass("Na2SO4")), # sodium sulfate |
|||
assert( 84.162; molar_mass("C6H12")), # cyclohexane |
|||
assert( 186.295; molar_mass("COOH(C(CH3)2)3CH3")), # butyric or butanoic acid |
|||
assert( 176.124; molar_mass("C6H4O2(OH)4")), # vitamin C ## |
|||
assert( 386.664; molar_mass("C27H46O")), # cholesterol |
|||
assert( 315 ; molar_mass("Uue")) # ununennium |
|||
; |
|||
</lang> |
|||
As mentioned above, a "debug" statement has been retained so that the parsed chemical formula can be seen. |
|||
{{out}} |
|||
<pre> |
|||
["DEBUG:",["H"]] |
|||
["DEBUG:",["H",2]] |
|||
["DEBUG:",["H",2,"O"]] |
|||
["DEBUG:",["H",2,"O",2]] |
|||
["DEBUG:",["Na",2,"S","O",4]] |
|||
["DEBUG:",["C",6,"H",12]] |
|||
["DEBUG:",["C","O","O","H",["C",["C","H",3],2],3,"C","H",3]] |
|||
["DEBUG:",["C",6,"H",4,"O",2,["O","H"],4]] |
|||
["DEBUG:",["C",27,"H",46,"O"]] |
|||
["DEBUG:",["Uue"]] |
|||
</pre> |
</pre> |
||