summaryrefslogtreecommitdiffstats
path: root/src/parser.hs
blob: f6883da5c6dcdbfd98768962e361372a183b462d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
module Parser where

import qualified Data.Bifunctor as B
import qualified Data.Hashable as DH
import Data.List (intercalate, isInfixOf, isPrefixOf)
import Data.Map (Map, (!))
import qualified Data.Map as M
import qualified Data.Text as T
import LTypes
import Utils

parseWord rawStr
  | isIntStr rawStr = LInteger (read rawStr)
  | isFloatStr rawStr = LFloat $ read rawStr
  | isCharStr rawStr = LChar $ rawStr !! 1
  | "##" `isPrefixOf` rawStr = LStringLitRef $ drop 2 rawStr
  | "$" `isPrefixOf` rawStr = LLabel $ tail rawStr
  | otherwise = LSymbol rawStr

removeComments source =
  let lines_ = lines source $> map (T.pack .> T.splitOn (T.pack "--") .> head .> T.unpack)
   in unlines lines_

processStringLiterals = processStringLiterals' Nothing M.empty

processStringLiterals' :: Maybe String -> Map String [LWord] -> String -> (String, Map String [LWord])
processStringLiterals' currentM refMap [] = case currentM of
  Just _ -> error "[error] nonterminated string literal"
  Nothing -> ([], refMap)
processStringLiterals' currentM refMap (c : source) = case c of
  '"' -> case currentM of
    Just str ->
      -- string literal ends
      let hash = DH.hash str $> show
          strP = str $> map LChar .> LPhrase
          interpolated = processSLInterpolations strP
          newRefMap = M.insert hash interpolated refMap
          (resSource, resRefMap) = processStringLiterals' Nothing newRefMap source
       in ("##" ++ hash ++ resSource, resRefMap)
    Nothing ->
      -- string literal starts
      processStringLiterals' (Just "") refMap source
  other -> case currentM of
    Just str -> processStringLiterals' (Just $ str ++ [c]) refMap source -- add char to current string literal
    Nothing -> processStringLiterals' Nothing refMap source $> B.first (c :) -- proceed normally

processSLInterpolations :: LWord -> [LWord]
processSLInterpolations strP@(LPhrase lChars)
  | "%%" `isInfixOf` chars = [LPhrase interpolated, LSymbol "unphrase"]
  | otherwise = [strP]
  where
    chars = lChars $> map (\(LChar c) -> c)
    tChars = T.pack chars
    separatedByMarker = T.pack chars $> T.splitOn (T.pack "%%") .> map (T.unpack .> map LChar)
    labelIndices = [0 .. length separatedByMarker - 1 - 1]
    varStores = labelIndices $> map (\n -> [LLabel ("$__" ++ show n), LSymbol "!"]) .> reverse
    part1 = concat varStores
    symbolLookups = labelIndices $> map (\n -> [LLabel ("$__" ++ show n), LSymbol "@", LSymbol "unphrase"])
    part2 = LSymbol "'[" : concat (mix separatedByMarker symbolLookups) ++ [LSymbol "']", LSymbol "phrase"]
    varForgets = labelIndices $> map (\n -> [LLabel ("$__" ++ show n), LSymbol "forget"])
    part3 = concat varForgets
    interpolated = part1 ++ part2 ++ part3
processSLInterpolations p = error $ "[error] non-phrase in processSLInterpolations: " ++ show p

parseSource source =
  let woComments = source $> removeComments
      (woStringLiterals, stringLiteralRefMap) = processStringLiterals woComments
   in (woStringLiterals $> words .> map parseWord, stringLiteralRefMap)