Skip to content

Commit

Permalink
Parse conversions as expressions, closes #28
Browse files Browse the repository at this point in the history
  • Loading branch information
sharkdp committed Mar 11, 2017
1 parent bcb431e commit 3bbd6a6
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 28 deletions.
16 changes: 11 additions & 5 deletions src/Insect/Interpreter.purs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import Data.Foldable (intercalate)
import Data.StrMap (lookup, insert, foldMap)
import Data.Bifunctor (lmap)

import Quantities (Quantity, DerivedUnit, UnificationError, asValueIn, pow,
import Quantities (Quantity, UnificationError, asValueIn, pow,
qNegate, qAdd, qDivide, qMultiply, qSubtract, quantity,
unity, convert, errorMessage, prettyPrint, fullSimplify)
unity, convert, errorMessage, prettyPrint, fullSimplify,
derivedUnit)

import Insect.Language (BinOp(..), Expression(..), Command(..), Statement(..))
import Insect.Environment (Environment, initialEnvironment)
Expand All @@ -41,8 +42,9 @@ qAdd' q1 q2 = lmap UnificationError (qAdd q1 q2)
asScalar Quantity Expect Number
asScalar q = lmap UnificationError (q `asValueIn` unity)

convert' DerivedUnit Quantity Expect Quantity
convert' d q = lmap UnificationError (convert d q)
convert' Quantity Quantity Expect Quantity
convert' target source = lmap UnificationError (convert targetUnit source)
where targetUnit = derivedUnit target

-- | Evaluate a mathematical expression involving physical quantities.
eval Environment Expression Expect Quantity
Expand All @@ -57,6 +59,11 @@ eval env (BinOp Pow x y) = do
exp ← eval env y
expNumber ← asScalar exp
pure $ base `pow` expNumber
eval env (BinOp ConvertTo s t) = do
source <- eval env s
target <- eval env t
res <- convert' target source
pure res
eval env (Variable name) =
case lookup name env of
Just q → pure q
Expand All @@ -79,7 +86,6 @@ message env (Right q) =
-- | Run a single statement of an Insect program.
runInsect Environment Statement { msg Message, newEnv Environment }
runInsect env (Expression e) = message env (fullSimplify <$> eval env e)
runInsect env (Conversion e u) = message env (eval env e >>= convert' u)
runInsect env (Assignment n v) =
case eval env v of
Left evalErr → message env (Left evalErr)
Expand Down
3 changes: 1 addition & 2 deletions src/Insect/Language.purs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ data BinOp
| Mul
| Div
| Pow
| ConvertTo

derive instance eqBinOpEq BinOp
derive instance genericBinOpGeneric BinOp
Expand Down Expand Up @@ -59,13 +60,11 @@ instance showCommand ∷ Show Command where show = gShow
-- | A statement in the Insect language.
data Statement
= Expression Expression
| Conversion Expression DerivedUnit
| Assignment Identifier Expression
| Command Command

derive instance eqStatementEq Statement
instance showStatementShow Statement where
show (Expression e) = "(Expression " <> show e <> ")"
show (Conversion e u) = "(Conversion " <> show e <> " " <> show u <> ")"
show (Assignment i e) = "(Assignment " <> show i <> " " <> show e <> ")"
show (Command c) = "(Command " <> show c <> ")"
39 changes: 21 additions & 18 deletions src/Insect/Parser.purs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Quantities (DerivedUnit, atto, bit, byte, centi, day, deci,

import Data.Either (Either)
import Data.Array (some, fromFoldable)
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Maybe (fromMaybe)
import Data.String (fromCharArray, singleton)
import Global (readFloat, isFinite)

Expand Down Expand Up @@ -222,15 +222,16 @@ term p = parens p <|> quantity <|> variable
expression P Expression
expression = fix \p →
buildExprParser
[ [ Infix (powOp $> BinOp Pow) AssocLeft ]
, [ Postfix (sqrOp $> square) ]
, [ Postfix (cubOp $> cube) ]
, [ Prefix (subOp $> Negate) ]
, [ Prefix (addOp $> id) ]
, [ Infix (divOp $> BinOp Div) AssocLeft ]
, [ Infix (mulOp $> BinOp Mul) AssocLeft ]
, [ Infix (subOp $> BinOp Sub) AssocLeft ]
, [ Infix (addOp $> BinOp Add) AssocLeft ]
[ [ Infix (powOp $> BinOp Pow) AssocLeft ]
, [ Postfix (sqrOp $> square) ]
, [ Postfix (cubOp $> cube) ]
, [ Prefix (subOp $> Negate) ]
, [ Prefix (addOp $> id) ]
, [ Infix (divOp $> BinOp Div) AssocLeft ]
, [ Infix (mulOp $> BinOp Mul) AssocLeft ]
, [ Infix (subOp $> BinOp Sub) AssocLeft ]
, [ Infix (addOp $> BinOp Add) AssocLeft ]
, [ Infix (convOp $> BinOp ConvertTo) AssocLeft ]
] (term p)
where
powOp = reservedOp "^" <|> reservedOp "**"
Expand All @@ -240,22 +241,21 @@ expression = fix \p →
mulOp = reservedOp "*" <|> reservedOp "·"
subOp = reservedOp "-"
addOp = reservedOp "+"
convOp = reservedOp "->"

two = Q 2.0 unity
square q = BinOp Pow q two
three = Q 3.0 unity
cube q = BinOp Pow q three

-- | Parse a mathematical expression (or conversion) like `3m` or `3m->ft`.
expressionOrConversion P Statement
expressionOrConversion = do
-- | Parse a mathematical expression (or conversion) like `3m`.
fullExpression P Expression
fullExpression = do
whiteSpace
expr ← expression
conv ← optionMaybe (reservedOp "->" *> derivedUnit <* whiteSpace)
eof <?> "end of input"

case conv of
Just target → pure $ Conversion expr target
Nothing → pure $ Expression expr
pure $ expr

-- | Parse an Insect-command
command P Command
Expand All @@ -278,7 +278,10 @@ assignment = do

-- | Parse a statement in the Insect language.
statement P Statement
statement = (Command <$> command) <|> try assignment <|> expressionOrConversion
statement =
(Command <$> command)
<|> try assignment
<|> (Expression <$> fullExpression)

-- | Run the Insect-parser on a `String` input.
parseInsect String Either ParseError Statement
Expand Down
6 changes: 3 additions & 3 deletions test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,13 @@ main = runTest do

suite "Parser - Conversions" do
test "Simple" do
allParseAs (Conversion (Q (2.3) meter) inch)
allParseAs (Expression (BinOp ConvertTo (Q (2.3) meter) (Q 1.0 inch)))
[ "2.3m -> in"
, " 2.3 meters->inches "
, " 2.3 m -> in "
]

allParseAs (Conversion (Q 120.0 minute) hour)
allParseAs (Expression (BinOp ConvertTo (Q 120.0 minute) (Q 1.0 hour)))
[ "120min -> h"
, "120minutes -> hours"
]
Expand All @@ -277,7 +277,7 @@ main = runTest do
shouldFail "2.3m->yikes"

test "Complex units" do
allParseAs (Conversion (Q 36.0 (kilo meter ./ hour)) (mile ./ hour))
allParseAs (Expression (BinOp ConvertTo (Q 36.0 (kilo meter ./ hour)) (Q 1.0 (mile ./ hour))))
[ "36km/h -> mph"
]

Expand Down

0 comments on commit 3bbd6a6

Please sign in to comment.