-- Copyright (c) 2025, Chris Pressey, Cat's Eye Technologies.
-- This file is distributed under a 2-clause BSD license, see LICENSES/ dir.
-- SPDX-License-Identifier: LicenseRef-BSD-2-Clause-X-Burro
module Language.Kondey.Definition where
data Kondey = Null
| ToggleHalt
| Inc
| Dec
| GoLeft
| GoRight
| Test Kondey Kondey
| Seq Kondey Kondey
| Cond [Kondey]
deriving (Read, Show, Eq)
-- Parse a Kondey program from a string
parse :: String -> Kondey
parse string =
let
(prog, _) = parseProgram string
in
trim prog
-- Main parsing function that returns (parsed program, remaining input)
parseProgram :: String -> (Kondey, String)
parseProgram [] = (Null, [])
parseProgram (c:cs) = case c of
'e' -> seqWith Null cs
'+' -> seqWith Inc cs
'-' -> seqWith Dec cs
'<' -> seqWith GoLeft cs
'>' -> seqWith GoRight cs
'!' -> seqWith ToggleHalt cs
'(' -> parseTest cs
'{' -> parseCond cs
'/' -> (Null, '/':cs) -- Stop at delimiter
'}' -> (Null, '}':cs) -- Stop at delimiter
')' -> (Null, ')':cs) -- Stop at delimiter
_ -> parseProgram cs -- Skip other characters
-- Helper to sequence a program with the rest
seqWith :: Kondey -> String -> (Kondey, String)
seqWith prog rest =
let
(next, remaining) = parseProgram rest
in
(Seq prog next, remaining)
-- Parse a test construct (a/b)
parseTest :: String -> (Kondey, String)
parseTest input =
let
(thenProg, rest1) = parseProgram input
(elseProg, rest2) = case rest1 of
'/':rest1' -> parseProgram rest1'
_ -> (Null, rest1)
rest3 = dropWhile (/= ')') rest2
rest4 = drop 1 rest3 -- Skip the ')'
in
(Test thenProg elseProg, rest4)
-- Parse a conditional construct {.../.../...}
parseCond :: String -> (Kondey, String)
parseCond input =
let
(branches, rest) = parseBranches input []
(next, remaining) = parseProgram rest
in
(Seq (Cond branches) next, remaining)
-- Parse branches in a conditional, accumulating them in acc
parseBranches :: String -> [Kondey] -> ([Kondey], String)
parseBranches input acc =
let
(prog, rest) = parseProgram input
in
case rest of
'/':rest' -> parseBranches rest' (acc ++ [prog])
'}':rest' -> (acc ++ [prog], rest')
_ -> error $ "Expected '/' or '}' in conditional, got: " ++ take 10 rest
-- Trim unnecessary Null elements from the AST
trim :: Kondey -> Kondey
trim (Seq Null a) = trim a
trim (Seq a Null) = trim a
trim (Seq a b) = Seq (trim a) (trim b)
trim (Test a b) = Test (trim a) (trim b)
trim (Cond bs) = Cond (map trim bs)
trim x = x