Skip to content

Commit

Permalink
more modern tool chain: stack. improve readme. fix most warnings.
Browse files Browse the repository at this point in the history
  • Loading branch information
EsGeh committed Feb 17, 2024
1 parent aaef17e commit 88d8779
Show file tree
Hide file tree
Showing 46 changed files with 887 additions and 455 deletions.
6 changes: 2 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
dist
.cabal-sandbox
cabal.sandbox.config
src/**/.*
.stack-work/
*~
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changelog for `sgCFG`

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to the
[Haskell Package Versioning Policy](https://pvp.haskell.org/).

## Unreleased

## 0.1.0.0 - YYYY-MM-DD
30 changes: 30 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Copyright Samuel Gfrörer (c) 2024

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.

* Neither the name of Author name here nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
113 changes: 113 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# sgCFG

## Description

sgCFG is a command line tool for processing context-free grammars, in particular applying transformations to a grammar.
A program can read and write grammars in various formats.
Also the details of the format can be modified via command line arguments.

## Example Use Case

There are many strategies for writing parsers based on a given context-free grammar.
In functional languages *parser combinators* such as [parsec](https://hackage.haskell.org/package/parsec) are a common and convenient approach, since the code for a parser may be (more or less) directly derived from a given grammar, maybe even automatically generated.
In such scenarios, the shape of the input grammar has an important influence on the effiency and even on termination properties of a parser.
One might find the need to transform or "optimise" a grammar beforehand (usually without changing the language).
This tool may be used to apply such transformations.

## Notable Features

- read/write a grammar in backus-naur format
- read/write a grammar using grouped format(A -> X1 | ... | Xn) or ungrouped format (A -> X1, ... , A -> Xn)
- apply transformations to a grammar, notably:

- left factoring
- left recursion elimination
- elimination of epsilon rules
- isolation of the grammar for a specific nonterminal
- finding unused rules
- calculation of "FIRST" sets as described in the famous [Dragonbook](#bibliography)

## Remark

This software is work in progress. Bug reports and pull requests are welcome.

## Prerequisits

- [stack](https://haskellstack.org)

## Build

$ stack build

## Installation

The executable can be run from a local directory:

$ stack exec -- sgCFG [ARGS...]

To be able to invoke sgCFG from the command line without having to type the whole path to the executable, issue

$ stack install

and make sure that `stack path --local-bin` is in your `PATH`. Now, the program may be run from the command line:

$ sgCFG [ARGS...]

## Usage

(this passage assumes that the sgCFG executable is in your PATH)

To print a list of OPTIONS, run:

$ sgCFG -h

The program is implemented after the well known "filter" paradigm used for many linux/unix utilites - it reads a grammar from stdin, and outputs a grammar to stdout (potentially after applying some transformations to it).
Also, it logs what is being done to stderr.

If you have a text file "file" containing your grammar, you can use sgCFG like this:

$ cat "file" | sgCFG --input-format=default --output-grouped=default [OPTIONS] 2>"logfile.log" > "outputfile"

*Please notice*: the options "--input-format" and "--output" (or "--output-grouped") are always needed!

## Examples

- Identity (no transformations), *default* output format

$ echo -e 'A -> A "a" | "a"' | stack exec -- sgCFG -i default -d default
A->
A "a"
A->
"a"

- Identity (no transformations), *grouped* output format

$ echo -e 'A -> A "a" | "a"' | stack exec -- sgCFG -i default -g default
A ->
A "a"
|"a"

- Eliminate Left Recursion

$ echo -e 'A -> A "a" | "a"' | stack exec -- sgCFG -i default -g default -t 'elimLeftRec(%v%n)' 2>/dev/null
A ->
"a" A0
A0 ->
"a" A0
|""

- Chaining multiple transformations: Eliminate Left Recursion > Eliminate Epsilon Rules:

$ echo -e 'A -> A "a" | "a"' | stack exec -- sgCFG -i default -g default -t 'elimLeftRec(%v%n)' -t 'elimEpsilon()' 2>/dev/null
A ->
"a" A0
|"a"
A0 ->
"a" A0
|"a

## Bibliography

[Dragonbook]
Alfred V. Aho, Monica S. Lam, Ravi Sethi, and Jeffrey D. Ullman:
Compilers: Principles, Techniques, and Tools
2 changes: 2 additions & 0 deletions Setup.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain
44 changes: 18 additions & 26 deletions src/ConfigFromArgs.hs → app/ConfigFromArgs.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,14 @@ module ConfigFromArgs(
) where

import Config.Types
--import Grammar
import GrammarFormat
import Types
import Utils (mapSnd)

import qualified System.Console.GetOpt as Opt
import Control.Monad
import Control.Monad.Except
--import Text.Read

--import Control.Applicative

configFromArgs :: MonadError String m => [String] -> m Config
configFromArgs args =
Expand All @@ -26,11 +23,9 @@ configFromArgs args =
let
f = foldl (>=>) return options
in
-- maybe (throwError "error parsing options") return $
f $ defConfig
f $ defConfig
(_,_,errMessages) ->
throwError $ "option format error: " ++ unlines errMessages
-- Nothing --errMessages

usageString :: String -> String
usageString progName =
Expand Down Expand Up @@ -100,8 +95,8 @@ optDescrList =
Opt.ReqArg transformation "GRAMMAR_TRANSFORMATION"
]
where
defineOption shortOpt longOpt descr transformation =
Opt.Option shortOpt longOpt transformation descr
defineOption shortOpt longOpt descr transformation' =
Opt.Option shortOpt longOpt transformation' descr

outputTree :: MonadError String m => Config -> m Config
outputTree =
Expand All @@ -111,17 +106,20 @@ outputTree =
case spec of
OutputGroupedGrammar info ->
return $ OutputGroupedGrammar $ info{ outputGrammar_asTree = True }
_ -> --Nothing
_ ->
throwError $ "bla"

mapToHeadOrError' ::
MonadError p m =>
p -> (a -> m a)
-> [a] -> m [a]
mapToHeadOrError' errMsg f list =
case list of
[] -> throwError $ errMsg
(x:xs) -> do
new <- f x
return $ new:xs


inputF :: MonadError String m => String -> Config -> m Config
inputF arg cfg =
do
Expand Down Expand Up @@ -151,6 +149,9 @@ changeInputFormat arg cfg = do
(key, val) <- either throwError return $ parseKeyValue arg
cfgMapToInputFormatM (applyFormatChange key val) cfg

parseKeyValue ::
FromPretty a =>
[Char] -> Either ParseError (a, [Char])
parseKeyValue str =
case mapSnd (drop 1) $ span (/='=') str of
(keyStr, val) -> do
Expand All @@ -172,9 +173,9 @@ transformation arg =
=<<
outputGrammarInfo_mapToTransformationsM (changeTransformations str) info
where
changeTransformations str list = do
transformation <- either throwError return $ fromPretty str
return $ (list++[transformation])
changeTransformations str' list = do
transformation' <- either throwError return $ fromPretty str'
return $ (list++[transformation'])
_ ->
throwError $ "not defined for grouped grammars yet!"

Expand All @@ -187,19 +188,15 @@ changeOutputFormat arg cfg =
where
changeF :: FormatParam -> String -> [OutputSpec] -> m [OutputSpec]
changeF key val outputCommands =
--maybe (throwError "outputCommands empty") return $
flip (mapToHeadOrError' "") outputCommands $
\spec ->
--(maybe (throwError "") return . flip mapToHeadMaybe outputCommands) =<< \spec ->
case spec of
OutputGrammar info ->
--return $
fmap OutputGrammar $
outputGrammarInfo_mapToFormatM (applyFormatChange key val) info
fmap OutputGrammar $
outputGrammarInfo_mapToFormatM (applyFormatChange key val) info
OutputGroupedGrammar info ->
--return $
fmap OutputGroupedGrammar $
outputGrammarInfo_mapToFormatM (applyFormatChange key val) info
fmap OutputGroupedGrammar $
outputGrammarInfo_mapToFormatM (applyFormatChange key val) info
_ ->
throwError $ "bla"

Expand Down Expand Up @@ -240,10 +237,5 @@ applyFormatChange param str outputInfo
FormatState
(f alreadySet format)
(if alreadySet then overwrittenParams else param:overwrittenParams)
{-
case param `elem` overwrittenParams of
False -> FormatState (f (const [str]) format) (param:overwrittenParams)
True -> FormatState (f (str:) format) overwrittenParams
-}
format = formatState_format outputInfo
overwrittenParams = formatState_paramsChanged outputInfo
21 changes: 8 additions & 13 deletions src/Main.hs → app/Main.hs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
module Main where
module Main (main) where

import ConfigFromArgs

import Parse.Token
import Grammar
import GroupedGrammar
import Types
import ConfigFromArgs
import Parse.ParseFormatFromGrammarFormat (parseFormatFromGrammarFormat)

import Utils.Logging

import qualified System.Environment as Env (getArgs, getProgName)
import Control.Monad
import Control.Monad.Except
--import Control.Monad.Writer
import Data.List
--import Data.Maybe(fromMaybe)
import Control.Monad.Trans
import Data.List( intercalate )


main :: IO ()
Expand Down Expand Up @@ -97,6 +97,9 @@ unpackTransformationMonad e =
--exit
Right x -> return x

tokStreamToText ::
(Foldable t, HasTokenType a, Pretty a) =>
t a -> [Char]
tokStreamToText s =
intercalate " " $
foldl conc [] $
Expand All @@ -107,11 +110,3 @@ tokStreamToText s =
case tokenType next of
SepTokenType -> [pretty next] ++ ["\n"]
_ -> [pretty next]

{-
failOrVal :: Monad m => String -> Either String a -> m a
failOrVal msg x =
case x of
Left err -> fail $ msg ++ err
Right val -> return val
-}
Loading

0 comments on commit 88d8779

Please sign in to comment.