1
0
Fork 0
agulator/Parse.agda

132 lines
4.6 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 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
val : Maybe A
rem : List Char
-- emit a result with a value and continue parsing
emit↓ : {A : Set} → A → List Char → Result A
emit↓ a rem = emit (just a) rem
-- emit a result without a value and backtrack
emit↑ : {A : Set} → List Char → Result A
emit↑ rem = emit nothing rem
-- result to string
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 val) rem = emit↓ (x ∷ val) rem
-- ignore consecutive characters
ignoreCons : List Char → List Char → List Char
ignoreCons _ [] = []
ignoreCons [] r = r
ignoreCons cs xs with takeCons cs xs
... | emit nothing rem = rem
... | emit (just val) rem = rem
digits : List Char
digits = primStringToList "0123456789"
opers : List Char
opers = primStringToList "-+*/"
skips : List Char
skips = 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 (ignoreCons skips s)
... | emit nothing rem₁ = emit↑ rem₁
... | emit (just xs) rem₁ with parseNat nothing xs
... | emit nothing rem₂ = emit↑ rem₁
... | emit (just n) rem₂ = emit↓ n rem₁
-- provided for completeness with the parse/take pair above, but this one is not used
parseOper : List Char → Result Token
parseOper [] = emit↑ []
parseOper (x ∷ xs) with parseChar x
... | Oper o = emit↓ (Oper o) xs
... | _ = emit↑ xs
takeOper : List Char → Result Token
takeOper s with takeCons opers (ignoreCons skips 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
-- this should maybe be its own module or something
data BinExpr : Set where
bin : Token → Token → Token → BinExpr
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)))