git @ Cat's Eye Technologies Burro / master src / Language / Kondey / Definition.hs
master

Tree @master (Download .tar.gz)

Definition.hs @masterraw · history · blame

-- 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