Skip to content

Commit 633d46a

Browse files
committed
add framework for passing options to the scad execution engine. also clean some warnings, and use the messaging infrastructure more consistently.
1 parent b1168e1 commit 633d46a

File tree

6 files changed

+100
-75
lines changed

6 files changed

+100
-75
lines changed

Graphics/Implicit/ExtOpenScad.hs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Prelude(String, Either(Left, Right), IO, ($), fmap, return)
1212

1313
import Graphics.Implicit.Definitions (SymbolicObj2, SymbolicObj3)
1414

15-
import Graphics.Implicit.ExtOpenScad.Definitions (VarLookup, Message(Message), MessageType(SyntaxError))
15+
import Graphics.Implicit.ExtOpenScad.Definitions (VarLookup, ScadOpts, Message(Message), MessageType(SyntaxError))
1616

1717
import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram)
1818

@@ -35,12 +35,12 @@ import Control.Monad.State (runStateT)
3535
import System.Directory (getCurrentDirectory)
3636

3737
-- | Small wrapper of our parser to handle parse errors, etc.
38-
runOpenscad :: String -> IO (VarLookup, [SymbolicObj2], [SymbolicObj3], [Message])
39-
runOpenscad source =
38+
runOpenscad :: ScadOpts -> String -> IO (VarLookup, [SymbolicObj2], [SymbolicObj3], [Message])
39+
runOpenscad scadOpts source =
4040
let
4141
initial = defaultObjects
4242
rearrange :: (t, CompState) -> (VarLookup, [SymbolicObj2], [SymbolicObj3], [Message])
43-
rearrange (_, (CompState (varlookup, ovals, _, messages))) = (varlookup, obj2s, obj3s, messages) where
43+
rearrange (_, (CompState (varlookup, ovals, _, messages, _))) = (varlookup, obj2s, obj3s, messages) where
4444
(obj2s, obj3s, _ ) = divideObjs ovals
4545
show' err = showErrorMessages "or" "unknown parse error" "expecting" "unexpected" "end of input" (errorMessages err)
4646
mesg e = Message SyntaxError (sourcePosition $ errorPos e) $ show' e
@@ -49,6 +49,6 @@ runOpenscad source =
4949
Right sts -> fmap rearrange
5050
$ (\sts' -> do
5151
path <- getCurrentDirectory
52-
runStateT sts' $ CompState (initial, [], path, [])
52+
runStateT sts' $ CompState (initial, [], path, [], scadOpts)
5353
)
5454
$ mapM_ runStatementI sts

Graphics/Implicit/ExtOpenScad/Definitions.hs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module Graphics.Implicit.ExtOpenScad.Definitions (ArgParser(AP, APTest, APBranch
1414
SourcePosition(SourcePosition),
1515
Message(Message),
1616
MessageType(..),
17+
ScadOpts(ScadOpts),
1718
lookupVarIn,
1819
collector) where
1920

@@ -143,7 +144,7 @@ instance Show OVal where
143144
show (OObj2 obj) = "<obj2: " ++ show obj ++ ">"
144145
show (OObj3 obj) = "<obj3: " ++ show obj ++ ">"
145146

146-
-- In order to not propagate Parsec or other modules around, create our own source position type for the AST.
147+
-- | In order to not propagate Parsec or other modules around, create our own source position type for the AST.
147148
data SourcePosition = SourcePosition
148149
{ sourceLine :: Fastℕ
149150
, sourceColumn :: Fastℕ
@@ -155,7 +156,7 @@ instance Show SourcePosition where
155156
show (SourcePosition line col []) = "line " ++ show line ++ ", column " ++ show col
156157
show (SourcePosition line col filePath) = "line " ++ show line ++ ", column " ++ show col ++ ", file " ++ filePath
157158

158-
-- | the types of messages the execution engine can send back to the application.
159+
-- | The types of messages the execution engine can send back to the application.
159160
data MessageType = Info
160161
| Debug
161162
| Trace
@@ -175,6 +176,16 @@ data Message = Message MessageType SourcePosition String
175176
instance Show Message where
176177
show (Message mtype pos text) = show mtype ++ " at " ++ show pos ++ ": " ++ text
177178

179+
-- | Options changing the behavior of the extended OpenScad engine.
180+
data ScadOpts = ScadOpts
181+
{ openScadCompatibility :: Bool
182+
}
183+
184+
instance Show ScadOpts where
185+
show (ScadOpts openScadCompat) =
186+
"ScadOpts openScadCompatibility: " ++
187+
show openScadCompat
188+
178189
-- | Apply a symbolic operator to a list of expressions, returning one big expression.
179190
-- Accepts a string for the operator, to simplify callers.
180191
collector :: String -> [Expr] -> Expr

Graphics/Implicit/ExtOpenScad/Eval/Statement.hs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
module Graphics.Implicit.ExtOpenScad.Eval.Statement (runStatementI) where
99

10-
import Prelude(Maybe(Just, Nothing), Bool(True, False), Either(Left, Right), FilePath, IO, (.), ($), show, putStrLn, concatMap, return, (++), fmap, reverse, fst, readFile)
10+
import Prelude(Maybe(Just, Nothing), Bool(True, False), Either(Left, Right), FilePath, IO, (.), ($), show, concatMap, return, (++), fmap, reverse, fst, readFile)
1111

1212
import Graphics.Implicit.ExtOpenScad.Definitions (
1313
Statement(Include, (:=), Echo, For, If, NewModule, ModuleCall, DoNothing),
@@ -16,12 +16,14 @@ import Graphics.Implicit.ExtOpenScad.Definitions (
1616
OVal(OString, OBool, OList, OModule),
1717
VarLookup(VarLookup),
1818
StatementI(StatementI),
19-
Symbol(Symbol)
19+
Symbol(Symbol),
20+
MessageType(Info),
21+
ScadOpts
2022
)
2123

2224
import Graphics.Implicit.ExtOpenScad.Util.OVal (getErrors)
2325
import Graphics.Implicit.ExtOpenScad.Util.ArgParser (argument, defaultTo, argMap)
24-
import Graphics.Implicit.ExtOpenScad.Util.StateC (StateC, CompState(CompState), errorC, modifyVarLookup, mapMaybeM, lookupVar, pushVals, getRelPath, withPathShiftedBy, getVals, putVals)
26+
import Graphics.Implicit.ExtOpenScad.Util.StateC (StateC, CompState(CompState), errorC, modifyVarLookup, mapMaybeM, lookupVar, pushVals, getRelPath, withPathShiftedBy, getVals, putVals, addMessage, scadOptions)
2527
import Graphics.Implicit.ExtOpenScad.Eval.Expr (evalExpr, matchPat)
2628
import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram)
2729

@@ -50,13 +52,15 @@ runStatementI (StatementI sourcePos (pat := expr)) = do
5052
(_, Just match) -> modifyVarLookup $ varUnion match
5153
(_, Nothing ) -> errorC sourcePos "pattern match failed in assignment"
5254

55+
-- FIXME: take scadOptions into account.
5356
runStatementI (StatementI sourcePos (Echo exprs)) = do
57+
opts <- scadOptions
5458
let
5559
show2 (OString s) = s
5660
show2 x = show x
5761
vals <- mapM evalExpr exprs
5862
case getErrors (OList vals) of
59-
Nothing -> liftIO . putStrLn $ concatMap show2 vals
63+
Nothing -> addMessage Info sourcePos $ concatMap show2 vals
6064
Just err -> errorC sourcePos err
6165

6266
runStatementI (StatementI sourcePos (For pat expr loopContent)) = do
@@ -80,10 +84,11 @@ runStatementI (StatementI sourcePos (If expr a b)) = do
8084
_ -> return ()
8185

8286
runStatementI (StatementI sourcePos (NewModule name argTemplate suite)) = do
87+
opts <- scadOptions
8388
argTemplate' <- forM argTemplate $ \(name', defexpr) -> do
8489
defval <- mapMaybeM evalExpr defexpr
8590
return (name', defval)
86-
(CompState (VarLookup varlookup, _, path, _)) <- get
91+
(CompState (VarLookup varlookup, _, path, _, scadOpts)) <- get
8792
-- FIXME: \_? really?
8893
runStatementI . StatementI sourcePos $ (Name name :=) $ LitE $ OModule $ \_ -> do
8994
newNameVals <- forM argTemplate' $ \(name', maybeDef) -> do
@@ -109,13 +114,14 @@ runStatementI (StatementI sourcePos (NewModule name argTemplate suite)) = do
109114
newNameVals' = newNameVals ++ [("children", children),("child", child), ("childBox", childBox)]
110115
-}
111116
varlookup' = union (fromList newNameVals) varlookup
112-
suiteVals = runSuiteCapture (VarLookup varlookup') path suite
117+
suiteVals = runSuiteCapture (VarLookup varlookup') path scadOpts suite
113118
return suiteVals
114119

115120
runStatementI (StatementI sourcePos (ModuleCall (Symbol name) argsExpr suite)) = do
121+
opts <- scadOptions
116122
maybeMod <- lookupVar (Symbol name)
117-
(CompState (varlookup, _, path, _)) <- get
118-
childVals <- fmap reverse . liftIO $ runSuiteCapture varlookup path suite
123+
(CompState (varlookup, _, path, _, _)) <- get
124+
childVals <- fmap reverse . liftIO $ runSuiteCapture varlookup path opts suite
119125
argsVal <- forM argsExpr $ \(posName, expr) -> do
120126
val <- evalExpr expr
121127
return (posName, val)
@@ -133,30 +139,26 @@ runStatementI (StatementI sourcePos (ModuleCall (Symbol name) argsExpr suite)) =
133139
return []
134140
pushVals newVals
135141

136-
runStatementI (StatementI _ (Include name injectVals)) = do
142+
runStatementI (StatementI sourcePos (Include name injectVals)) = do
137143
name' <- getRelPath name
138144
content <- liftIO $ readFile name'
139145
case parseProgram name' content of
140-
Left e -> liftIO $ putStrLn $ "Error parsing " ++ name ++ ":" ++ show e
146+
Left e -> errorC sourcePos $ "Error parsing " ++ name ++ ":" ++ show e
141147
Right sts -> withPathShiftedBy (takeDirectory name) $ do
142148
vals <- getVals
143149
putVals []
144150
runSuite sts
145151
vals' <- getVals
146152
if injectVals then putVals (vals' ++ vals) else putVals vals
147153

148-
runStatementI (StatementI _ DoNothing) = liftIO $ putStrLn "Do Nothing?"
154+
runStatementI (StatementI _ DoNothing) = return ()
149155

150156
runSuite :: [StatementI] -> StateC ()
151157
runSuite = mapM_ runStatementI
152158

153-
runSuiteCapture :: VarLookup -> FilePath -> [StatementI] -> IO [OVal]
154-
runSuiteCapture varlookup path suite = do
159+
runSuiteCapture :: VarLookup -> FilePath -> ScadOpts -> [StatementI] -> IO [OVal]
160+
runSuiteCapture varlookup path opts suite = do
155161
(res, _) <- runStateT
156162
(runSuite suite >> getVals)
157-
(CompState (varlookup, [], path, []))
163+
(CompState (varlookup, [], path, [], opts))
158164
return res
159-
160-
161-
162-

Graphics/Implicit/ExtOpenScad/Util/StateC.hs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
{-# LANGUAGE KindSignatures, FlexibleContexts #-}
1010
{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
1111

12-
module Graphics.Implicit.ExtOpenScad.Util.StateC (addMessage, getVarLookup, modifyVarLookup, lookupVar, pushVals, getVals, putVals, withPathShiftedBy, getPath, getRelPath, errorC, mapMaybeM, StateC, CompState(CompState)) where
12+
module Graphics.Implicit.ExtOpenScad.Util.StateC (addMessage, getVarLookup, modifyVarLookup, lookupVar, pushVals, getVals, putVals, withPathShiftedBy, getPath, getRelPath, errorC, mapMaybeM, StateC, CompState(CompState), scadOptions) where
1313

1414
import Prelude(FilePath, IO, String, Maybe(Just, Nothing), Show, Monad, fmap, (.), ($), (++), return, putStrLn, show)
1515

16-
import Graphics.Implicit.ExtOpenScad.Definitions(VarLookup(VarLookup), OVal, Symbol, SourcePosition, Message(Message), MessageType(Error))
16+
import Graphics.Implicit.ExtOpenScad.Definitions(VarLookup(VarLookup), OVal, Symbol, SourcePosition, Message(Message), MessageType(Error), ScadOpts)
1717

1818
import Data.Map (lookup)
1919
import Control.Monad.State (StateT, get, put, modify, liftIO)
@@ -22,57 +22,63 @@ import Control.Monad.IO.Class (MonadIO)
2222
import Data.Kind (Type)
2323

2424
-- | This is the state of a computation. It contains a hash of variables, an array of OVals, a path, and messages.
25-
newtype CompState = CompState (VarLookup, [OVal], FilePath, [Message])
25+
newtype CompState = CompState (VarLookup, [OVal], FilePath, [Message], ScadOpts)
2626

2727
type StateC = StateT CompState IO
2828

2929
getVarLookup :: StateC VarLookup
30-
getVarLookup = fmap (\(CompState (a,_,_,_)) -> a) get
30+
getVarLookup = fmap (\(CompState (a,_,_,_,_)) -> a) get
3131

3232
modifyVarLookup :: (VarLookup -> VarLookup) -> StateC ()
33-
modifyVarLookup = modify . (\f (CompState (a,b,c,d)) -> CompState (f a, b, c, d))
33+
modifyVarLookup = modify . (\f (CompState (a,b,c,d,e)) -> CompState (f a, b, c, d, e))
3434

3535
-- | Perform a variable lookup
36+
-- FIXME: generate a warning when we look up a variable that is not present.
3637
lookupVar :: Symbol -> StateC (Maybe OVal)
3738
lookupVar name = do
3839
(VarLookup varlookup) <- getVarLookup
3940
return $ lookup name varlookup
4041

4142
pushVals :: [OVal] -> StateC ()
42-
pushVals vals = modify (\(CompState (a,b,c,d)) -> CompState (a, vals ++ b, c, d))
43+
pushVals vals = modify (\(CompState (a,b,c,d,e)) -> CompState (a, vals ++ b, c, d, e))
4344

4445
getVals :: StateC [OVal]
4546
getVals = do
46-
(CompState (_,b,_,_)) <- get
47+
(CompState (_,b,_,_,_)) <- get
4748
return b
4849

4950
putVals :: [OVal] -> StateC ()
5051
putVals vals = do
51-
(CompState (a,_,c,d)) <- get
52-
put $ CompState (a,vals,c,d)
52+
(CompState (a,_,c,d,e)) <- get
53+
put $ CompState (a,vals,c,d,e)
5354

5455
withPathShiftedBy :: FilePath -> StateC a -> StateC a
5556
withPathShiftedBy pathShift s = do
56-
(CompState (a,b,path,d)) <- get
57-
put $ CompState (a, b, path </> pathShift,d)
57+
(CompState (a,b,path,d,e)) <- get
58+
put $ CompState (a, b, path </> pathShift, d, e)
5859
x <- s
59-
(CompState (a',b',_,d')) <- get
60-
put $ CompState (a', b', path, d')
60+
(CompState (a',b',_,d',e')) <- get
61+
put $ CompState (a', b', path, d', e')
6162
return x
6263

6364
-- | Return the path stored in the state.
6465
getPath :: StateC FilePath
6566
getPath = do
66-
(CompState (_,_,c,_)) <- get
67+
(CompState (_,_,c,_,_)) <- get
6768
return c
6869

6970
getRelPath :: FilePath -> StateC FilePath
7071
getRelPath relPath = do
7172
path <- getPath
7273
return $ path </> relPath
7374

75+
scadOptions :: StateC ScadOpts
76+
scadOptions = do
77+
(CompState (_, _, _, _, opts)) <- get
78+
return opts
79+
7480
addMesg :: Message -> StateC ()
75-
addMesg = modify . (\message (CompState (a, b, c, messages)) -> (CompState (a, b, c, messages ++ [message])))
81+
addMesg = modify . (\message (CompState (a, b, c, messages, d)) -> (CompState (a, b, c, messages ++ [message], d)))
7682

7783
addMessage :: MessageType -> SourcePosition -> String -> StateC ()
7884
addMessage mtype pos text = addMesg $ Message mtype pos text

programs/extopenscad.hs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
-- Let's be explicit about what we're getting from where :)
1313

14-
import Prelude (Read(readsPrec), Maybe(Just, Nothing), IO, FilePath, Show, Eq, String, (++), ($), (*), (/), (==), (>), (**), (-), readFile, minimum, drop, error, map, fst, min, sqrt, tail, take, length, putStrLn, show, (>>=), lookup, return, unlines)
14+
import Prelude (Read(readsPrec), Maybe(Just, Nothing), IO, Bool, FilePath, Show, Eq, String, (++), ($), (*), (/), (==), (>), (**), (-), readFile, minimum, drop, error, map, fst, min, sqrt, tail, take, length, putStrLn, show, (>>=), lookup, return, unlines)
1515

1616
-- Our Extended OpenScad interpreter, and functions to write out files in designated formats.
1717
import Graphics.Implicit (runOpenscad, writeSVG, writeDXF2, writeBinSTL, writeOBJ, writeSCAD2, writeSCAD3, writeGCodeHacklabLaser, writePNG2, writePNG3)
@@ -34,17 +34,18 @@ import Data.Tuple (swap)
3434
-- Functions and types for dealing with the types used by runOpenscad.
3535

3636
-- The definition of the symbol type, so we can access variables, and see the requested resolution.
37-
import Graphics.Implicit.ExtOpenScad.Definitions (VarLookup, OVal(ONum), lookupVarIn, Message)
37+
import Graphics.Implicit.ExtOpenScad.Definitions (VarLookup, OVal(ONum), lookupVarIn, Message, ScadOpts(ScadOpts))
3838

3939
-- Operator to subtract two points. Used when defining the resolution of a 2d object.
4040
import Data.AffineSpace ((.-.))
4141

42+
-- For defining the <> operator.
4243
import Data.Monoid (Monoid, mappend)
4344

4445
import Control.Applicative ((<$>), (<*>))
4546

4647
-- NOTE: make sure we don't import (<>) in new versions.
47-
import Options.Applicative (fullDesc, progDesc, header, auto, info, helper, help, str, argument, long, short, option, metavar, execParser, Parser, optional, strOption)
48+
import Options.Applicative (fullDesc, progDesc, header, auto, info, helper, help, str, argument, long, short, option, metavar, execParser, Parser, optional, strOption, switch)
4849

4950
-- For handling input/output files.
5051
import System.FilePath (splitExtension)
@@ -65,6 +66,7 @@ data ExtOpenScadOpts = ExtOpenScadOpts
6566
, resolution :: Maybe
6667
, inputFile :: FilePath
6768
, messageOutputFile :: Maybe FilePath
69+
, openScadCompatibility :: Bool
6870
}
6971

7072
-- | A type serving to enumerate our output formats.
@@ -136,11 +138,16 @@ extOpenScadOpts = ExtOpenScadOpts
136138
<*> optional (
137139
strOption
138140
( short 'e'
139-
<> long "echo-output"
140-
<> metavar "FILE"
141-
<> help "Output file name for echo statements"
141+
<> long "echo-output"
142+
<> metavar "FILE"
143+
<> help "Output file name for echo statements"
142144
)
143145
)
146+
<*> switch
147+
( short 'O'
148+
<> long "fopenscad-compat"
149+
<> help "Favour compatibility with OpenSCAD semantics, where they are incompatible with ExtOpenScad semantics"
150+
)
144151

145152
-- | Try to look up an output format from a supplied extension.
146153
readOutputFormat :: String -> Maybe OutputFormat
@@ -223,7 +230,8 @@ run args = do
223230
_ | Just fmt <- outputFormat args -> Just fmt
224231
_ | Just file <- outputFile args -> Just $ guessOutputFormat file
225232
_ -> Nothing
226-
openscadProgram = runOpenscad content
233+
scadOpts = ScadOpts (openScadCompatibility args)
234+
openscadProgram = runOpenscad scadOpts content
227235

228236
putStrLn "Processing File."
229237

0 commit comments

Comments
 (0)