Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support input of hex, octal, and binary numbers #339

Merged
merged 1 commit into from
Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ Contents

- **Exponential notation**: `6.022e23`.

- **Hexadecimal, octal and binary number input**:

0xFFFF
0b1011
0o32
0x2.F
0o5p3

- **Physical units**: parsing and handling, including metric prefixes:

2 min + 30 s
Expand Down
10 changes: 10 additions & 0 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ Documentation

* **Exponential notation**: `6.022e23`.

* **Hexadecimal, octal and binary number input**:

```
0xFFFF
0b1011
0o32
0x2.F
0o5p3
```

- **Physical units**: parsing and handling, including metric prefixes:

```
Expand Down
45 changes: 27 additions & 18 deletions src/Insect/Parser.purs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ import Data.Maybe (Maybe(..), fromMaybe)
import Data.NonEmpty (NonEmpty, (:|))
import Data.Semigroup.Foldable (foldl1, foldr1)
import Data.String (fromCodePointArray, codePointFromChar, singleton)
import Data.Tuple.Nested ((/\))
import Insect.Environment (Environment, StoredFunction(..))
import Insect.Language (BinOp(..), Expression(..), Command(..), Statement(..), Identifier)
import Quantities (DerivedUnit, (./))
import Quantities as Q
import Parsing (ParserT, Parser, ParseError, runParser, fail)
import Parsing.Combinators (option, optionMaybe, try, (<?>), notFollowedBy)
import Parsing.String (string, char, eof)
import Parsing.String.Basic (oneOf)
import Parsing.String.Basic (hexDigit, octDigit, oneOf)
import Parsing.Token (GenLanguageDef(..), LanguageDef, TokenParser, digit, letter, makeTokenParser)

-- | A type synonym for the main Parser type with `String` as input.
Expand Down Expand Up @@ -90,21 +91,15 @@ whiteSpace = token.whiteSpace
-- | Parse a number.
number ∷ P Decimal
number = do
decimalPart ← fractionalPart <|> do
intPart ← digits
mFracPart ← optionMaybe fractionalPart
pure (intPart <> fromMaybe "" mFracPart)
numberPrefix /\ digit' /\ expSymbol ←
option ("" /\ digit /\ "e") $ try $ char '0'
*> ((char 'x' <|> char 'X') $> ("0x" /\ hexDigit /\ "p")
<|> (char 'o' <|> char 'O') $> ("0o" /\ octDigit /\ "p")
<|> (char 'b' <|> char 'B') $> ("0b" /\ (oneOf ['0', '1'] <?> "binary digit") /\ "p"))

mExpPart ← optionMaybe $ try do
_ ← string "e"
notFollowedBy identStart
sad ← signAndDigits
pure ("e" <> sad)
let expPart = fromMaybe "" mExpPart

whiteSpace
numberWithoutPrefix ← number' digit' expSymbol

let floatStr = decimalPart <> expPart
let floatStr = numberPrefix <> numberWithoutPrefix

case fromString floatStr of
Just num →
Expand All @@ -113,11 +108,25 @@ number = do
else fail "This number is too large"
Nothing → fail $ "Parsing of number failed for input '" <> floatStr <> "'"

number' ∷ P Char → String → P String
number' digit' expSymbol = do
decimalPart ← fractionalPart <|> do
intPart ← digits
mFracPart ← optionMaybe fractionalPart
pure (intPart <> fromMaybe "" mFracPart)

expPart ← option "" $ try do
_ ← string expSymbol
notFollowedBy identStart
sad ← signAndDigits
pure (expSymbol <> sad)

whiteSpace

pure $ decimalPart <> expPart
where
digits ∷ P String
digits = do
ds ← some $ oneOf ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] <?> "a digit"
pure $ fromCharArray (fromFoldable ds)
digits = (fromCharArray <<< fromFoldable) <$> some digit'

fractionalPart ∷ P String
fractionalPart = (<>) <$> string "." <*> digits
Expand All @@ -127,7 +136,7 @@ number = do
signAndDigits ∷ P String
signAndDigits = do
sign ← option '+' (oneOf ['+', '-'])
intPart ← digits
intPart ← (fromCharArray <<< fromFoldable) <$> some digit
pure $ singleton (codePointFromChar sign) <> intPart

-- | A helper type for entries in the dictionary.
Expand Down
55 changes: 55 additions & 0 deletions test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,48 @@ main = runTest do
shouldFail "123e+"
shouldFail "123e-"

test "Hexadecimal, octal and binary numbers" do
allParseAs (Expression (scalar 106.0))
[ "0x6A"
, "0X6a"
, "0b1101010"
, "0B1101010"
, "0o152"
, "0O152"
]

allParseAs (Expression (scalar 256.0))
[ "0x4p6"
, "0b100p6"
, "0o10p5"
]

allParseAs (Expression (Negate (scalar 3.0)))
[ "-0x3"
, "-0b11"
, "-0o3"
]

allParseAs (Expression (scalar 1.0))
[ "0x2p-1"
, "0b10p-1"
, "0o2p-1"
]

shouldParseAs (Expression (BinOp Add (scalar 12.75) (BinOp Sub (scalar 13.125) (scalar 18.5))))
"0xC.C + 0o15.1 - 0b10.01010p3"

shouldParseAs (Expression (scalar 481.65625)) "0x1E1.a8"

shouldFail "0xG"
shouldFail "0oG"
shouldFail "0oA"
shouldFail "0b2"
shouldFail "0be"
shouldFail "0x1p+"
shouldFail "0x1p-"
shouldFail "0x5.."
Comment on lines +188 to +195
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👍


suite "Parser - Units (this may take some time)" do
test "Simple" do
allParseAs (Expression (Unit meter))
Expand Down Expand Up @@ -657,6 +699,19 @@ main = runTest do
, "2 e"
]

test "Variables which begin with 'p'" do
allParseAs (Expression (BinOp Mul (scalar 2.0) (Variable "pi")))
[ "0x2pi"
, "0o2.0pi"
, "0b10 pi"
]

allParseAs (Expression (BinOp Mul (scalar 2.0) (Variable "p")))
[ "0b10p"
, "0x2.0p"
, "0o2 p"
]

test "Variables before parenthesis" do
allParseAs (Expression (BinOp Mul (Variable "pi") (scalar 2.0)))
[ "pi(2)"
Expand Down