mirror of
https://github.com/catseye/SixtyPical.git
synced 2024-11-24 15:32:27 +00:00
Initial import.
This commit is contained in:
commit
650405c2fe
4
build.sh
Executable file
4
build.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
mkdir -p bin
|
||||
ghc src/Main.hs -o bin/sixtypical
|
14
eg/compare.60pical
Normal file
14
eg/compare.60pical
Normal file
@ -0,0 +1,14 @@
|
||||
reserve byte m_high
|
||||
reserve byte m_low
|
||||
reserve byte n_high
|
||||
reserve byte n_low
|
||||
|
||||
routine compare_16_bit {
|
||||
lda m_high
|
||||
cmp n_high
|
||||
beq {
|
||||
lda m_low
|
||||
cmp n_low
|
||||
} else {
|
||||
}
|
||||
}
|
19
eg/simple.60pical
Normal file
19
eg/simple.60pical
Normal file
@ -0,0 +1,19 @@
|
||||
assign word fnord 4000
|
||||
assign byte blerf 4002
|
||||
|
||||
reserve byte foo
|
||||
reserve word bar
|
||||
|
||||
routine hello {
|
||||
lda fnord
|
||||
cmp blerf
|
||||
lda foo
|
||||
}
|
||||
|
||||
routine bye {
|
||||
lda fnord
|
||||
cmp bar
|
||||
}
|
||||
|
||||
routine byee {
|
||||
}
|
260
src/Main.hs
Normal file
260
src/Main.hs
Normal file
@ -0,0 +1,260 @@
|
||||
-- encoding: UTF-8
|
||||
|
||||
module Main where
|
||||
--module Sixtype where
|
||||
|
||||
import qualified Data.Map as Map
|
||||
|
||||
import System.IO
|
||||
import System.Environment
|
||||
import System.Exit
|
||||
|
||||
import Text.ParserCombinators.Parsec
|
||||
|
||||
-- -- -- -- machine model -- -- -- --
|
||||
|
||||
type Address = Int -- LET'S ASSUME THIS IS AT LEAST 16 BITS
|
||||
|
||||
type LocationName = String
|
||||
|
||||
data Register = A | X | Y -- | MemLoc LocationName
|
||||
deriving (Show, Ord, Eq)
|
||||
|
||||
allRegisters = [A, X, Y]
|
||||
|
||||
-- -- -- -- program model -- -- -- --
|
||||
|
||||
data Size = Byte
|
||||
| Word
|
||||
deriving (Show, Ord, Eq)
|
||||
|
||||
data Decl = Assign LocationName Size Address -- .alias
|
||||
| Reserve LocationName Size -- .word, .byte
|
||||
deriving (Show, Ord, Eq)
|
||||
|
||||
type RoutineName = String
|
||||
|
||||
data Instruction = LOAD Register LocationName
|
||||
| COPY Register Register
|
||||
| CMP Register LocationName
|
||||
| JSR RoutineName
|
||||
| IFEQ [Instruction] [Instruction]
|
||||
| NOP
|
||||
deriving (Show, Ord, Eq)
|
||||
|
||||
data Routine = Routine RoutineName [Instruction]
|
||||
deriving (Show, Ord, Eq)
|
||||
|
||||
data Program = Program [Decl] [Routine]
|
||||
deriving (Show, Ord, Eq)
|
||||
|
||||
-- -- -- -- data-flow-analysis context -- -- -- --
|
||||
|
||||
data Usage = Unknown
|
||||
| Value LocationName -- obviously a bit daft for now
|
||||
| Retained Register
|
||||
deriving (Show, Ord, Eq)
|
||||
|
||||
type RoutineContext = Map.Map Register Usage
|
||||
|
||||
type ProgramContext = Map.Map RoutineName RoutineContext
|
||||
|
||||
--
|
||||
-- Utility function:
|
||||
-- Take 2 routine contexts -- the current routine and a routine that was just
|
||||
-- JSR'ed to (immediately previously) -- and merge them to create a new
|
||||
-- context for the current routine.
|
||||
--
|
||||
mergeRoutCtxs routCtx calledRoutCtx =
|
||||
let
|
||||
-- insert the values into routCtx
|
||||
-- TODO, first compare them
|
||||
-- TODO, if not equal, 'poison' them
|
||||
-- TODO, other special cases (eg Unknown)
|
||||
poison key value routCtxAccum =
|
||||
case value of
|
||||
-- if the called routine retains it,
|
||||
-- we keep our idea of it -- but TODO
|
||||
-- should we mark it "was retained"?
|
||||
Retained reg ->
|
||||
routCtxAccum
|
||||
_ ->
|
||||
Map.insert key value routCtxAccum
|
||||
in
|
||||
Map.foldrWithKey (poison) routCtx calledRoutCtx
|
||||
|
||||
-- -- -- -- static analyzer -- -- -- --
|
||||
|
||||
checkProgram (Program decls routines) =
|
||||
checkRoutines routines Map.empty
|
||||
|
||||
checkRoutines [] progCtx = progCtx
|
||||
checkRoutines (rout@(Routine name _) : routs) progCtx =
|
||||
let
|
||||
routCtx = Map.fromList $ map (\reg -> (reg, Retained reg)) allRegisters
|
||||
routAnalysis = checkRoutine rout progCtx routCtx
|
||||
progCtx' = Map.insert name routAnalysis progCtx
|
||||
in
|
||||
checkRoutines routs progCtx'
|
||||
|
||||
checkRoutine (Routine _ []) progCtx routCtx = routCtx
|
||||
checkRoutine (Routine name (instr : instrs)) progCtx routCtx =
|
||||
let
|
||||
routCtx' = checkInstr instr progCtx routCtx
|
||||
in
|
||||
checkRoutine (Routine name instrs) progCtx routCtx'
|
||||
|
||||
checkInstr (LOAD reg addr) progCtx routCtx =
|
||||
Map.insert reg (Value addr) routCtx
|
||||
checkInstr (COPY src dst) progCtx routCtx =
|
||||
Map.insert dst (Map.findWithDefault Unknown src routCtx) routCtx
|
||||
checkInstr (JSR name) progCtx routCtx =
|
||||
case Map.lookup name progCtx of
|
||||
Just calledRoutCtx ->
|
||||
mergeRoutCtxs routCtx calledRoutCtx
|
||||
Nothing ->
|
||||
error ("can't call routine '" ++ name ++ "' before it is defined")
|
||||
checkInstr (CMP reg addr) progCtx routCtx =
|
||||
-- TODO: mark Carry bit as "touched" here
|
||||
routCtx
|
||||
checkInstr (IFEQ b1 b2) progCtx routCtx =
|
||||
-- TODO: oooh, this one's gonna be fun
|
||||
routCtx
|
||||
checkInstr NOP progCtx routCtx =
|
||||
routCtx
|
||||
|
||||
-- -- -- -- parser -- -- -- --
|
||||
{-
|
||||
|
||||
Toplevel := {Decl} {Routine}.
|
||||
Decl := "reserve" Size LocationName
|
||||
| "assign" Size LocationName Address.
|
||||
Size := "byte" | "word".
|
||||
Routine := "routine" RoutineName Block.
|
||||
Block := "{" {Command} "}".
|
||||
Command := "beq" Block "else" Block
|
||||
| "lda" (LocationName | Immediate)
|
||||
| "txa" | "tax" | "tya" | "tay"
|
||||
| "cmp" (LocationName | Immediate)
|
||||
|
||||
-}
|
||||
|
||||
toplevel :: Parser Program
|
||||
toplevel = do
|
||||
decls <- many (assign <|> try reserve)
|
||||
routines <- many routine
|
||||
return $ Program decls routines
|
||||
|
||||
reserve :: Parser Decl
|
||||
reserve = do
|
||||
string "reserve"
|
||||
spaces
|
||||
sz <- size
|
||||
spaces -- size does not do its own spacesising
|
||||
name <- locationName
|
||||
return $ Reserve name sz
|
||||
|
||||
assign :: Parser Decl
|
||||
assign = do
|
||||
string "assign"
|
||||
spaces
|
||||
sz <- size
|
||||
spaces -- size does not do its own spacesising
|
||||
name <- locationName
|
||||
addr <- address
|
||||
return $ Assign name sz addr
|
||||
|
||||
size :: Parser Size
|
||||
size = do
|
||||
s <- (string "byte") <|> (string "word")
|
||||
return $ case s of
|
||||
"byte" -> Byte
|
||||
"word" -> Word
|
||||
|
||||
routine :: Parser Routine
|
||||
routine = do
|
||||
string "routine"
|
||||
spaces
|
||||
name <- routineName
|
||||
instrs <- block
|
||||
return (Routine name instrs)
|
||||
|
||||
block :: Parser [Instruction]
|
||||
block = do
|
||||
string "{"
|
||||
spaces
|
||||
cs <- many command
|
||||
string "}"
|
||||
spaces
|
||||
return cs
|
||||
|
||||
command :: Parser Instruction
|
||||
command = cmp <|> lda <|> beq
|
||||
|
||||
cmp :: Parser Instruction
|
||||
cmp = do
|
||||
string "cmp"
|
||||
spaces
|
||||
l <- locationName
|
||||
return (CMP A l)
|
||||
|
||||
lda :: Parser Instruction
|
||||
lda = do
|
||||
string "lda"
|
||||
spaces
|
||||
l <- locationName
|
||||
return (LOAD A l)
|
||||
|
||||
beq :: Parser Instruction
|
||||
beq = do
|
||||
string "beq"
|
||||
spaces
|
||||
b1 <- block
|
||||
string "else"
|
||||
spaces
|
||||
b2 <- block
|
||||
return (IFEQ b1 b2)
|
||||
|
||||
routineName :: Parser String
|
||||
routineName = do
|
||||
c <- letter
|
||||
cs <- many (alphaNum <|> char '_')
|
||||
spaces
|
||||
return (c:cs)
|
||||
|
||||
locationName :: Parser String
|
||||
locationName = do
|
||||
c <- letter
|
||||
cs <- many (alphaNum <|> char '_')
|
||||
spaces
|
||||
return (c:cs)
|
||||
|
||||
address :: Parser Address
|
||||
address = do
|
||||
digits <- many digit
|
||||
spaces
|
||||
return (read digits :: Address)
|
||||
|
||||
-- -- -- -- driver -- -- -- --
|
||||
|
||||
main = do
|
||||
args <- getArgs
|
||||
case args of
|
||||
[filename] -> do
|
||||
programText <- readFile filename
|
||||
case parse toplevel "" programText of
|
||||
Right program -> do
|
||||
putStrLn $ show $ program
|
||||
putStrLn $ show $ checkProgram program
|
||||
Left problem -> do
|
||||
hPutStrLn stderr (show problem)
|
||||
exitWith $ ExitFailure 1
|
||||
_ -> do
|
||||
putStrLn "Usage: sixtypical filename.60pical"
|
||||
exitWith $ ExitFailure 1
|
||||
|
||||
{-
|
||||
test = checkProgram [(Routine "wait" [LOAD Y "score", COPY Y A]),
|
||||
(Routine "main" [LOAD X "score", JSR "wait"])]
|
||||
Map.empty
|
||||
-}
|
Loading…
Reference in New Issue
Block a user