Echo Writes Code

Main.hs

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import Orchid.Tokenizer (runTokenizer)
import Orchid.Parser (runParser)
import Orchid.Data (Token(..), renderToken, describeToken, Statement(..))

import qualified Data.Text.IO.Utf8 as Text.IO
import Data.Text (Text)

import qualified Options.Applicative as Options
import Options.Applicative ((<**>))

import qualified System.IO

import qualified Text.Colour.Chunk as Colors
import qualified Text.Colour.Capabilities as TerminalCapabilities

data Arguments =
	TokenizeArguments { sourceFile :: Maybe String, outputFile :: Maybe String } |
	ParseArguments { sourceFile :: Maybe String, outputFile :: Maybe String }

makeArgumentParser :: Options.ParserInfo Arguments
makeArgumentParser = Options.info
	(parseArguments <**> Options.helper) (
		Options.fullDesc <>
		Options.progDesc "Transforms an Orchid source file into another form" <>
		Options.header "orchid-cli: The Orchid language command-line interface")

parseArguments :: Options.Parser Arguments
parseArguments = do
	let tokenize = do
		sourceFile <- parseSourceFile
		outputFile <- parseOutputFile
		return TokenizeArguments { .. }

	let parse = do
		sourceFile <- parseSourceFile
		outputFile <- parseOutputFile
		return ParseArguments { .. }

	Options.hsubparser (
		Options.command "tokenize" (Options.info tokenize (Options.progDesc "Tokenize an Orchid source file")) <>
		Options.command "parse" (Options.info parse (Options.progDesc "Parse an Orchid source file")))

parseSourceFile :: Options.Parser (Maybe String)
parseSourceFile = Options.optional $ Options.strArgument (Options.metavar "SOURCE_FILE" <> Options.help "The source file to process")

parseOutputFile :: Options.Parser (Maybe String)
parseOutputFile = Options.optional $ Options.strOption (Options.short 'o' <> Options.long "output-file" <> Options.metavar "OUTPUT_FILE" <> Options.help "The output file to process")

main :: IO ()
main = do
	arguments <- Options.execParser makeArgumentParser
	runOrchidToolchain arguments

runOrchidToolchain :: Arguments -> IO ()

runOrchidToolchain (TokenizeArguments sourceFile outputFile) = do
	source <- readSourceFile sourceFile
	let result = runTokenizer (filenameOf sourceFile) source
	case result of
		Left e -> putStrLn e
		Right tokens -> renderTokens outputFile tokens

runOrchidToolchain (ParseArguments sourceFile outputFile) = do
	source <- readSourceFile sourceFile
	let tokenizeResult = runTokenizer (filenameOf sourceFile) source
	case tokenizeResult of
		Left e -> putStrLn e
		Right tokens -> do
			let parseResult = runParser (filenameOf sourceFile) tokens
			case parseResult of
				Left e -> putStrLn e
				Right statements -> renderStatements outputFile statements

filenameOf :: Maybe String -> String
filenameOf (Just filename) = filename
filenameOf Nothing = "<standard input>"

readSourceFile :: Maybe String -> IO Text
readSourceFile (Just sourceFile) = Text.IO.readFile sourceFile
readSourceFile Nothing = Text.IO.getContents

renderTokens :: Maybe String -> [Token] -> IO ()
renderTokens outputFile tokens = do
	outputHandle <- case outputFile of
		Just path -> System.IO.openFile path System.IO.WriteMode
		Nothing -> return System.IO.stdout
	mapM_ (Text.IO.hPutStrLn outputHandle . displayToken) tokens
	System.IO.hClose outputHandle

renderStatements :: Maybe String -> [Statement] -> IO ()
renderStatements outputFile statements = do
	outputHandle <- case outputFile of
		Just path -> System.IO.openFile path System.IO.WriteMode
		Nothing -> return System.IO.stdout
	mapM_ (Text.IO.hPutStrLn outputHandle . displayStatement) statements
	System.IO.hClose outputHandle

displayToken :: Token -> Text
displayToken token = "> " <> description <> ": " <> colorRepresentation representation where
	representation = renderToken token
	description = describeToken token

displayStatement :: Statement -> Text
displayStatement _ = "placeholder"

colorRepresentation :: Text -> Text
colorRepresentation text = Colors.renderChunkText TerminalCapabilities.With8Colours colored where
	colored = Colors.fore Colors.cyan chunk
	chunk = Colors.chunk text