1
0
Fork 0
agulator/Parse.agda

121 lines
4.5 KiB
Plaintext

module Parse where
open import Agda.Builtin.Bool
open import Agda.Builtin.Char
open import Agda.Builtin.List
open import Agda.Builtin.Maybe
open import Agda.Builtin.Nat
open import Agda.Builtin.String
open import Data.List using (_++_; reverse)
open import Data.Nat.DivMod using (_/_)
open import Util
data Token : Set where
Digit : Nat → Token
Delim : Char → Token
Oper : Char → Token
Skip : Char → Token
Term : Token
record Result (A : Set) : Set where
constructor emit
field
res : Maybe A
rem : List Char
emit↓ : {A : Set} → A → List Char → Result A
emit↓ a rem = emit (just a) rem
emit↑ : {A : Set} → List Char → Result A
emit↑ rem = emit nothing rem
showResult : {A : Set} → (A → String) → Result A → String
showResult f (emit nothing rem) = primStringAppend "remainder: " (primStringFromList rem)
showResult f (emit (just r) []) = primStringAppend "result: " (f r)
showResult f (emit (just r) xs) = primStringAppend (primStringAppend "result: " (f r)) (primStringAppend ", remainder: " (primStringFromList xs))
-- take consecutive occurences of a character set
takeCons : List Char → List Char → Result (List Char)
takeCons _ [] = emit↑ []
takeCons [] r = emit↑ r
takeCons cs (x ∷ xs) with (findCharIndex 0 x cs)
... | nothing = emit↑ (x ∷ xs)
... | just n with (takeCons cs xs)
... | emit nothing rem = emit↓ (x ∷ []) xs
... | emit (just res) rem = emit↓ (x ∷ res) rem
digits : List Char
digits = primStringToList "0123456789"
opers : List Char
opers = primStringToList "-+*/"
-- parse a single character into a typed token
parseChar : Char → Token
parseChar '0' = Digit 0
parseChar '1' = Digit 1
parseChar '2' = Digit 2
parseChar '3' = Digit 3
parseChar '4' = Digit 4
parseChar '5' = Digit 5
parseChar '6' = Digit 6
parseChar '7' = Digit 7
parseChar '8' = Digit 8
parseChar '9' = Digit 9
parseChar ',' = Delim ','
parseChar '-' = Oper '-'
parseChar '+' = Oper '+'
parseChar '*' = Oper '*'
parseChar '/' = Oper '/'
parseChar ' ' = Skip ' '
parseChar _ = Term
-- parse a number from a list of characters
parseNat : Maybe Nat → List Char → Result Nat
parseNat a [] = emit a []
parseNat a (x ∷ xs) with parseChar x
... | Digit n = parseNat (just (((default 0 a) * 10) + n)) xs
... | _ = emit↑ xs
takeNat : List Char → Result Nat
takeNat s with takeCons digits s
... | emit nothing rem₁ = emit nothing rem₁
... | emit (just xs) rem₁ with parseNat nothing xs
... | emit nothing rem₂ = emit↑ rem₁
... | emit (just n) rem₂ = emit↓ n rem₁
takeOper : List Char → Result Token
takeOper s with takeCons opers s
... | emit nothing rem = emit↑ rem
... | emit (just []) rem = emit↑ rem
... | emit (just (x ∷ xs)) rem with parseChar x
... | Oper o = emit↓ (Oper o) (xs ++ rem)
... | _ = emit↑ s
data BinExpr : Set where
bin : Token → Token → Token → BinExpr
evalBin : Result BinExpr → Result Nat
evalBin (emit nothing rem) = emit↑ rem
evalBin (emit (just (bin (Oper '+') (Digit a) (Digit b))) rem) = emit↓ (a + b) rem
evalBin (emit (just (bin (Oper '-') (Digit a) (Digit b))) rem) = emit↓ (a - b) rem
evalBin (emit (just (bin (Oper '*') (Digit a) (Digit b))) rem) = emit↓ (a * b) rem
evalBin (emit (just (bin (Oper '/') (Digit a) (Digit b))) rem) with (b == zero)
... | false = emit↓ (a / (suc (b - 1))) rem -- todo: why tho
... | true = emit↑ rem
evalBin (emit (just (bin _ _ _)) rem) = emit↑ rem
takeBin : List Char → Result BinExpr
takeBin s with takeNat s
... | emit nothing rem₁ = emit↑ s
... | emit (just res₁) rem₁ with takeOper rem₁
... | emit nothing rem₂ = emit↑ rem₁
... | emit (just oper) rem₂ with takeNat rem₂
... | emit nothing rem₃ = emit↑ rem₁
... | emit (just res₃) rem₃ = emit↓ (bin oper (Digit res₁) (Digit res₃)) rem₃
takeLine : List Char → List (Result BinExpr)
takeLine s = map takeBin (map reverse (reverse (split (';' ∷ []) s)))