/
Compiler.hs
1327 lines (1104 loc) · 58.5 KB
/
Compiler.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Main where
import GoLexer
import GoParser
import Control.Monad
import Control.Monad.State.Strict
import Control.Applicative
import Control.Arrow (first, second)
import qualified Data.Map as M
import Data.Maybe (isJust, isNothing, fromMaybe)
import Data.Char (ord, isUpper)
import Data.List
import Data.Either (rights)
import Control.Exception
import System.Environment (getArgs)
import System.Console.GetOpt
import System.IO
import System.Exit
-- alright, thoughts:
-- * need a symbol table to hold things we know about.
-- * each symbol has a type.
-- * there's a stack of symbol tables with the innermost scope at the top.
-- * functions and variables can coexist in the symbol table
-- * should the symbol table include tracking what's stored in each register?
-- * register handling
-- * what to do when we run out -- should be rare enough to get away with just failing to compile, at least until v1.1
-- * optimization: keeping things in registers persistently instead of always fetching them from the stack.
-- * that's tricky because it requires analyzing whole blocks and functions instead of just a line or expression at a time.
-- * leaving that aside for now.
-- * thanks to the [SP+next word] argument types, locals and stack arguments are probably easily accessed in one instruction.
-- * might require two passes, once to determine the registers that are needed and how widely variables are used
-- * register reservation mechanism?
-- * without the above optimization, only really necessary to hold arguments (A-C) and the intermediate results of expressions
-- * return values go in A, but that can wait until the end.
-- * labels
-- * functions: _go10c_package_function
-- * globals: _go10c_package_varName
-- * jumps: _go10c_package_function_uniqueNumber
-- * that last means we need a global label number in a state monad.
type SymbolTable = M.Map QualIdent Type
data CompilerState = CS {
symbols :: [SymbolTable]
,dirtyRegs :: [String] -- registers that have been dirtied in a function and need cleaning up.
,freeRegs :: [String] -- registers that are currently in use and can't be used now.
,strings :: [(String,String)] -- a collection of string literals to be written into the binary. pairs are (symbol name, content).
,types :: SymbolTable
,args :: [(QualIdent, Location)] -- a list of argument names and their locations.
,locals :: [(QualIdent, Location)] -- a list of local variables and their locations.
,globals :: [(QualIdent, Location)] -- a list of global variables and their locations.
,this :: String -- the name of the function currently being compiled, used for labels.
,unique :: Int -- an incrementing number to allow the construction of globally unique labels.
,packageName :: String -- the package name for the file being compiled
} deriving (Show)
data Location = LocReg String
| LocStack Int -- places above the frame pointer, J
| LocLabel String -- in the given label
| LocConstant Int
deriving (Show)
newtype Compiler a = Compiler (StateT CompilerState IO a)
deriving (Functor, Applicative, Monad, MonadState CompilerState, MonadIO)
runCompiler :: Compiler a -> CompilerState -> IO (a, CompilerState)
runCompiler (Compiler a) s = runStateT a s
-- ADT for the output opcodes, to allow for pattern matched optimizations
data Asm = SET Arg Arg
| ADD Arg Arg
| SUB Arg Arg
| MUL Arg Arg
| MLI Arg Arg
| DIV Arg Arg
| DVI Arg Arg
| MOD Arg Arg
| MDI Arg Arg
| AND Arg Arg
| BOR Arg Arg
| XOR Arg Arg
| SHR Arg Arg
| ASR Arg Arg
| SHL Arg Arg
| IFB Arg Arg
| IFC Arg Arg
| IFE Arg Arg
| IFN Arg Arg
| IFG Arg Arg
| IFA Arg Arg
| IFL Arg Arg
| IFU Arg Arg
-- ADX, SBX, STI and STD are not used.
| JSR Arg
-- INT, IAG, IAS, RFI, IAQ, HWN, HWQ and HWI are not used.
| HCF -- arg is unused, compiles a literal 0.
| LabelDef String
| DAT String -- DAT statement with the literal asm following
data Arg = Reg String -- used only for main registers A-J
| PUSH
| POP
| PEEK
| SP
| PC
| EX
| Lit Int
| AddrLit Int
| AddrReg String -- main registers only!
| AddrRegLit String Int -- likewise
| AddrLabel String
| Label String
deriving (Eq)
instance Show Asm where
show (JSR a) = "JSR " ++ show a
show HCF = "HCF 0"
show (LabelDef s) = ":" ++ s
show (DAT s) = "DAT " ++ s
show (SET b a) = "SET " ++ show b ++ ", " ++ show a
show (ADD b a) = "ADD " ++ show b ++ ", " ++ show a
show (SUB b a) = "SUB " ++ show b ++ ", " ++ show a
show (MUL b a) = "MUL " ++ show b ++ ", " ++ show a
show (MLI b a) = "MLI " ++ show b ++ ", " ++ show a
show (DIV b a) = "DIV " ++ show b ++ ", " ++ show a
show (DVI b a) = "DVI " ++ show b ++ ", " ++ show a
show (MOD b a) = "MOD " ++ show b ++ ", " ++ show a
show (MDI b a) = "MDI " ++ show b ++ ", " ++ show a
show (AND b a) = "AND " ++ show b ++ ", " ++ show a
show (BOR b a) = "BOR " ++ show b ++ ", " ++ show a
show (XOR b a) = "XOR " ++ show b ++ ", " ++ show a
show (SHR b a) = "SHR " ++ show b ++ ", " ++ show a
show (ASR b a) = "ASR " ++ show b ++ ", " ++ show a
show (SHL b a) = "SHL " ++ show b ++ ", " ++ show a
show (IFB b a) = "IFB " ++ show b ++ ", " ++ show a
show (IFC b a) = "IFC " ++ show b ++ ", " ++ show a
show (IFE b a) = "IFE " ++ show b ++ ", " ++ show a
show (IFN b a) = "IFN " ++ show b ++ ", " ++ show a
show (IFG b a) = "IFG " ++ show b ++ ", " ++ show a
show (IFA b a) = "IFA " ++ show b ++ ", " ++ show a
show (IFL b a) = "IFL " ++ show b ++ ", " ++ show a
show (IFU b a) = "IFU " ++ show b ++ ", " ++ show a
instance Show Arg where
show (Reg s) = s
show PUSH = "PUSH"
show POP = "POP"
show PEEK = "PEEK"
show SP = "SP"
show PC = "PC"
show EX = "EX"
show (Lit n) = show n
show (AddrLit n) = "[" ++ show n ++ "]"
show (AddrReg r) = "[" ++ r ++ "]"
show (AddrRegLit r n) = "[" ++ r ++ "+" ++ show n ++ "]"
show (AddrLabel s) = "[" ++ s ++ "]"
show (Label s) = s
-- shorthand for a binary "opcode" like IFG or ADD, used in expression helper functions.
type Opcode = Arg -> Arg -> Asm
doCompile :: SourceFile -> [String] -> IO [Asm]
doCompile (SourceFile thePackage imports statements_) libdirs = do
-- handle the imports
allImports <- evalStateT (importsClosure imports (if null libdirs then ["."] else libdirs)) []
let statements = allImports ++ fixSelectors statements_
let allGlobals = findVariables statements
allConstants = findConstants statements
allTypes = M.fromList $ findTypes statements ++ builtinTypes
globalCode = flip concatMap allGlobals $ \(QualIdent mp i, t) -> [LabelDef (mkLabelInternal (fromMaybe thePackage mp) i)] ++ replicate (typeSizeInternal allTypes t) (DAT "0") -- include the label and enough space for the global
-- a function called main becomes the start point.
-- if we have a main, compile a jump to it as the first bit of code.
-- if we don't, compile an HCF 0.
allSymbols = findSymbols statements
startCode = case lookup (QualIdent Nothing "main") allSymbols of
Nothing -> [HCF]
Just _ -> [SET PC (Label (mkLabelInternal thePackage "main"))]
cs = CS {
symbols = [ M.fromList allSymbols ],
dirtyRegs = [],
freeRegs = [],
strings = [],
types = allTypes,
args = [],
locals = [],
globals = map (\(g@(QualIdent mp i), _) -> (g, LocLabel (mkLabelInternal (fromMaybe thePackage mp) i))) allGlobals ++ allConstants,
this = "",
unique = 1,
packageName = thePackage
}
(compiledCode, finalState) <- runCompiler (concat <$> mapM compile statements) cs
let stringsCode = concatMap (\(label, str) -> [LabelDef label, DAT $ "\"" ++ str ++ "\", 0"]) (strings finalState)
return $ startCode ++ globalCode ++ stringsCode ++ compiledCode
type ImportMonad a = StateT [String] IO a
-- maintains a list of imports already imported as the state, returns all the top-level declarations from all the imports, in dependency order.
importsClosure :: [Import] -> [String] -> ImportMonad [Statement]
importsClosure [] _ = return []
importsClosure (Import malias file : rest) libdirs = do
seen <- seenImport file
exports <- case seen of
True -> return []
False -> do
fileAttempts <- liftIO $ mapM (\d -> loadFile (d ++ "/" ++ file ++ ".go")) libdirs -- try to look up the file in each directory in libdirs
SourceFile package innerImports statements <- case rights fileAttempts of
[] -> error $ "Could not find import named '" ++ file ++ "'. Search paths: " ++ unwords libdirs
(s:_) -> return s
modify (file:)
recursiveExports <- importsClosure innerImports libdirs
let exports = exportedStatements package statements
return $ recursiveExports ++ exports
laterExports <- importsClosure rest libdirs
return $ exports ++ laterExports
--start here
seenImport :: String -> ImportMonad Bool
seenImport i = do
seen <- get
case filter (==i) seen of
(_:_) -> return True
[] -> return False
-- returns the subset of statements from a list of statements which are exportable
-- note that only functions and globals (and constants) with uppercase first letters are exported.
exportedStatements :: String -> [Statement] -> [Statement]
exportedStatements _ [] = []
exportedStatements pkg (StmtTypeDecl (QualIdent Nothing i) t : rest) | isUpper (head i) = StmtTypeDecl (QualIdent (Just pkg) i) t : exportedStatements pkg rest
exportedStatements pkg (StmtVarDecl (QualIdent Nothing i) t x : rest) | isUpper (head i) = StmtVarDecl (QualIdent (Just pkg) i) t x : exportedStatements pkg rest
exportedStatements pkg (StmtConstDecl (QualIdent Nothing i) mt x : rest) | isUpper (head i) = StmtConstDecl (QualIdent (Just pkg) i) mt x : exportedStatements pkg rest
exportedStatements pkg (StmtShortVarDecl (QualIdent Nothing i) x : rest) | isUpper (head i) = StmtShortVarDecl (QualIdent (Just pkg) i) x : exportedStatements pkg rest
exportedStatements pkg (StmtFunction (QualIdent Nothing i) args ret _ : rest) | isUpper (head i) = StmtFunction (QualIdent (Just pkg) i) args ret Nothing : exportedStatements pkg rest
exportedStatements pkg (_:rest) = exportedStatements pkg rest
-- All built-in types: int, uint, char, string, bool.
builtinTypes :: [(QualIdent, Type)]
builtinTypes = [(QualIdent Nothing "string", TypeString)
,(QualIdent Nothing "int", TypeInt)
,(QualIdent Nothing "uint", TypeUint)
,(QualIdent Nothing "char", TypeChar)
,(QualIdent Nothing "bool", TypeBool)
]
-- returns the type of a symbol, dying with an error if it's not found.
lookupSymbol :: QualIdent -> Compiler Type
lookupSymbol i = do
ms <- maybeLookupSymbol i
case ms of
Nothing -> error $ "unknown identifier: " ++ show i
Just t -> return t
-- returns the type of a symbol, or Nothing if it's not found.
maybeLookupSymbol :: QualIdent -> Compiler (Maybe Type)
maybeLookupSymbol i = do
syms <- gets symbols
let hits = map (M.lookup i) syms
case filter isJust hits of
(x:_) -> return x
_ -> return Nothing
-- Looks up the location where a variable is stored. Locals trump args trump globals.
lookupLocation :: QualIdent -> Compiler Location
lookupLocation i = do
s <- get
let res = map (lookup i) [locals s, args s, globals s]
case dropWhile isNothing res of
[] -> error $ "Could not find a storage location for the variable " ++ show i
(Just loc:_) -> return loc
-- Adds a symbol to the innermost scope.
addSymbol :: QualIdent -> Type -> Compiler ()
addSymbol i t = modify $ \s -> s { symbols = (M.insert i t (head (symbols s))) : tail (symbols s) }
-- Does a deep search to find all the local variables in a list of statements.
findLocals :: [Statement] -> [QualIdent]
findLocals [] = []
findLocals (StmtVarDecl i _ _ : rest) = i : findLocals rest
findLocals (StmtShortVarDecl i _ : rest) = i : findLocals rest
findLocals (StmtIf initializer _ ifblock elseblock : rest) = findLocals initializer ++ findLocals ifblock ++ findLocals elseblock ++ findLocals rest
findLocals (StmtFor initializer _ incrementer block : rest) = findLocals initializer ++ findLocals incrementer ++ findLocals block ++ findLocals rest
findLocals (StmtSwitch initializer _ cases : rest) = findLocals initializer ++ concatMap (findLocals.snd) cases ++ findLocals rest
findLocals (_:rest) = findLocals rest
-- finds variables and functions in a list of statements, intended for use on a whole file.
findSymbols :: [Statement] -> [(QualIdent, Type)]
findSymbols [] = []
findSymbols (StmtVarDecl s t _ : rest) = (s, t) : findSymbols rest
findSymbols (StmtConstDecl s t _ : rest) = (s, t) : findSymbols rest
findSymbols (StmtFunction name args ret _ : rest) = (name, TypeFunction (map snd args) ret) : findSymbols rest
findSymbols (_:rest) = findSymbols rest
-- finds variables with a shallow search. intended to find globals in the whole file.
findVariables :: [Statement] -> [(QualIdent, Type)]
findVariables [] = []
findVariables (StmtVarDecl i@(QualIdent Nothing _) t _ : rest) = (i, t) : findVariables rest
findVariables (_:rest) = findVariables rest
-- finds constants with a shallow search. intended to find global constants in the whole file.
findConstants :: [Statement] -> [(QualIdent, Location)]
findConstants [] = []
findConstants (StmtConstDecl i@(QualIdent Nothing _) _ (Just (LitInt x)) : rest) = (i, LocConstant x) : findConstants rest
findConstants (StmtConstDecl i@(QualIdent Nothing _) _ (Just _) : rest) = compileError $ "Only integer literals are supported as contants."
findConstants (StmtConstDecl _ _ Nothing : rest) = compileError $ "const declarations must include a value"
findConstants (_:rest) = findConstants rest
-- finds type declarations in a list of statements, intended for use on a whole file.
findTypes :: [Statement] -> [(QualIdent, Type)]
findTypes [] = []
findTypes (StmtTypeDecl s t : rest) = case t of
(TypeStruct fields) -> if null (filter (isUpper.head.fst) fields) then (s, t) : findTypes rest else error "Struct fields must begin with a lowercase letter."
_ -> (s, t) : findTypes rest
findTypes (_:rest) = findTypes rest
-- Finds every Var expression in the file, converting it to a Selector if the letter after the . is lowercase.
mapExpressions :: (Expr -> Expr) -> [Statement] -> [Statement]
mapExpressions f (StmtVarDecl i t (Just x) : rest) = StmtVarDecl i t (Just (f x)) : mapExpressions f rest
mapExpressions f (StmtShortVarDecl i x : rest) = StmtShortVarDecl i (f x) : mapExpressions f rest
mapExpressions f (StmtFunction i args ret (Just stmts) : rest) = StmtFunction i args ret (Just (mapExpressions f stmts)) : mapExpressions f rest
mapExpressions f (StmtExpr x : rest) = StmtExpr (f x) : mapExpressions f rest
mapExpressions f (StmtInc x : rest) = StmtInc (f x) : mapExpressions f rest
mapExpressions f (StmtDec x : rest) = StmtDec (f x) : mapExpressions f rest
mapExpressions f (StmtAssignment s lv rv : rest) = StmtAssignment s (f lv) (f rv) : mapExpressions f rest
mapExpressions f (StmtIf initializer condition ifbody elsebody : rest) =
StmtIf (mapExpressions f initializer) (f condition) (mapExpressions f ifbody) (mapExpressions f elsebody) : mapExpressions f rest
mapExpressions f (StmtFor initializer condition incrementer body : rest) =
StmtFor (mapExpressions f initializer) (f condition) (mapExpressions f incrementer) (mapExpressions f body) : mapExpressions f rest
mapExpressions f (StmtSwitch initializer switcher cases : rest) = StmtSwitch (mapExpressions f initializer) (f switcher) (map (\(xs, stmts) -> (map f xs, mapExpressions f stmts)) cases) : mapExpressions f rest
mapExpressions f (StmtReturn (Just x) : rest) = StmtReturn (Just (f x)) : mapExpressions f rest
mapExpressions f (s:rest) = s : mapExpressions f rest
mapExpressions _ [] = []
fixSelectors :: [Statement] -> [Statement]
fixSelectors = mapExpressions fixer
where fixer (Var (QualIdent (Just p) n)) | not (isUpper (head n)) = Selector (Var (QualIdent Nothing p)) n
fixer (LitComposite t vals) = LitComposite t (map (second fixer) vals)
fixer (Selector x s) = Selector (fixer x) s
fixer (Index x i) = Index (fixer x) (fixer i)
fixer (Call f args) = Call (fixer f) (map fixer args)
fixer (BuiltinCall t mt args) = BuiltinCall t mt (map fixer args)
fixer (Conversion t x) = Conversion t (fixer x)
fixer (UnOp t x) = UnOp t (fixer x)
fixer (BinOp t l r) = BinOp t (fixer l) (fixer r)
fixer x = x
-- returns a free register to be used in an expression.
getReg :: Compiler String
getReg = do
rs <- gets freeRegs
ds <- gets dirtyRegs
case rs of
[] -> error "Internal compiler error: Ran out of registers to allocate"
(r:rest) -> do
case filter (==r) ds of
[] -> modify $ \s -> s { dirtyRegs = r : ds, freeRegs = rest }
_ -> modify $ \s -> s { freeRegs = rest }
return r
freeReg :: String -> Compiler ()
freeReg r = modify $ \s -> s { freeRegs = r : freeRegs s }
-- turns a function name into a compiler label
mkLabel :: QualIdent -> Compiler String
mkLabel (QualIdent (Just p) s) = return $ mkLabelInternal p s
mkLabel (QualIdent Nothing s) = do
pkg <- gets packageName
return $ mkLabelInternal pkg s
mkLabelInternal :: String -> String -> String
mkLabelInternal p s = "_go10c__" ++ p ++ "_" ++ s
uniqueLabel :: Compiler String
uniqueLabel = do
s <- get
let label = this s ++ "_" ++ show (unique s)
put s { unique = unique s + 1 }
return label
compileError = error
typeError = error
-- typechecks an expression, returning its Type or printing a type error and exiting.
typeCheck :: Expr -> Compiler Type
typeCheck (LitInt _) = return TypeUint
typeCheck (LitChar _) = return TypeChar
typeCheck (LitBool _) = return TypeBool
typeCheck (LitString _) = return TypeString
typeCheck (LitComposite t _) = return t
typeCheck (Var (QualIdent Nothing "nil")) = return TypeNil
typeCheck (Var i) = lookupSymbol i
typeCheck (Selector x field) = do
xt <- typeCheck x
ut <- underlyingType xt
case ut of
(TypeStruct fields) -> case filter ((==field) . fst) fields of
[] -> typeError $ "Struct does not have a field named '" ++ field ++ "'."
[(_,t)] -> return t
_ -> error $ "The impossible just happened! Struct with multiple fields named '" ++ field ++ "'."
(TypePointer (TypeStruct fields)) -> case filter ((==field) . fst) fields of
[] -> typeError $ "Struct does not have a field named '" ++ field ++ "'."
[(_,t)] -> return t
_ -> error $ "The impossible just happened! Struct with multiple fields named '" ++ field ++ "'."
_ -> typeError $ "Attempt to select a field from a non-struct value " ++ show x ++ ", type " ++ show xt
typeCheck (Index x i) = do
arrType <- typeCheck x
indexType <- typeCheck i
case (arrType, indexType) of
(TypeArray t, TypeInt) -> return t
(TypeArray t, TypeUint) -> return t
(TypeArray _, t) -> typeError $ "Array index has type " ++ show t ++ ", not an integer."
(TypeName t@(QualIdent Nothing "string"), TypeInt) -> return TypeChar
(TypeName t@(QualIdent Nothing "string"), TypeUint) -> return TypeChar
(TypeName (QualIdent Nothing "string"), t) -> typeError $ "Array index has type " ++ show t ++ ", not an integer."
(t, _) -> typeError $ "Attempt to index non-array type " ++ show t ++ "."
typeCheck (Call f args) = do
ft <- typeCheck f
case ft of
(TypeFunction argTypes returnType) -> check argTypes returnType
(TypePointer (TypeFunction argTypes returnType)) -> check argTypes returnType
_ -> typeError $ "Attempt to call a non-function value " ++ show f ++ " of type " ++ show ft
where check argTypes returnType = do
providedArgTypes <- mapM typeCheck args
let zipped = zip argTypes providedArgTypes
assigns <- sequence $ map (uncurry assignable) zipped
let mismatched = map snd $ dropWhile fst $ zip assigns zipped
case mismatched of
[] -> return returnType -- function call is legal
((expected, actual):_) -> typeError $ "Function call expected an argument of type " ++ show expected ++ " but got " ++ show actual ++ "."
typeCheck (BuiltinCall LNew (Just (TypeArray t)) [n]) = do
nt <- typeCheck n
nut <- underlyingType nt
when (nut /= TypeInt) $ typeError $ "new() called to create an array type with non-int size of type " ++ show nt
return (TypeArray t) -- arrays are already pointers, we don't need to create a pointer to them.
typeCheck (BuiltinCall LNew (Just (TypeArray t)) _) = typeError $ "new() called to create an array but no size was provided"
typeCheck (BuiltinCall LNew (Just t) _) = return (TypePointer t) -- otherwise create a pointer to the provided type (TODO does this work for strings?)
typeCheck (BuiltinCall LNew Nothing [Var t]) = do
ut <- underlyingType (TypeName t)
case ut of
(TypeArray _) -> typeError $ "new() called to create an array but no size was provided."
ty -> return (TypePointer ty)
typeCheck (BuiltinCall LDelete Nothing [x]) = do
t <- typeCheck x
ut <- underlyingType t
when (not (isPointer ut) && not (isArray ut)) $ typeError $ "delete() called with a value that is neither a pointer nor an array. It has type " ++ show t
return TypeVoid -- delete returns nothing.
typeCheck (BuiltinCall LDelete Nothing _) = typeError $ "delete() must have an argument provided"
typeCheck (BuiltinCall LPanic _ _) = return TypeVoid -- panic ignores its arguments and HCFs
typeCheck (Conversion t x) = do
provided <- typeCheck x
canConvert <- convertible provided t
case canConvert of
True -> return t
False -> typeError $ "Cannot convert from " ++ show provided ++ " to " ++ show t ++ "."
typeCheck (UnOp (LOp op) x) = let
unopInt = do
xt <- typeCheck x
case xt of
TypeInt -> return TypeInt
_ -> typeError $ "Unary " ++ op ++ " expects an int, found " ++ show xt
in case op of
"+" -> unopInt
"^" -> unopInt
"-" -> case x of
(LitInt _) -> return TypeInt
t -> unopInt
"!" -> do
xt <- typeCheck x
case xt of
TypeBool -> return TypeBool
_ -> typeError $ "Unary ! expects a bool, found " ++ show xt
"*" -> do
xt <- typeCheck x
case xt of
TypePointer t -> return t
_ -> typeError $ "Unary * expects a pointer, found " ++ show xt
"&" -> TypePointer <$> typeCheck x
typeCheck (BinOp (LOp op) left right) = do
leftType <- underlyingType =<< typeCheck left
rightType <- underlyingType =<< typeCheck right
case op of
"+" -> do
case (leftType, rightType) of
(TypeString, TypeString) -> return TypeString
(TypeInt, TypeInt) -> return TypeInt
_ -> do
when (leftType /= TypeInt && leftType /= TypeString) $ typeError $ "Left argument of + must be int or string, but found " ++ show leftType
when (rightType /= TypeInt && rightType /= TypeString) $ typeError $ "right argument of + must be int or string, but found " ++ show rightType
when (leftType /= rightType) $ typeError $ "Left and right arguments of + do not match.\n\tLeft: " ++ show leftType ++ "\n\tRight: " ++ show rightType
typeError "Can't happen: Exhausted cases for typechecking binary +."
"-" -> typeCheckBinOp "-" leftType TypeInt rightType TypeInt TypeInt
"*" -> typeCheckBinOp "*" leftType TypeInt rightType TypeInt TypeInt
"/" -> typeCheckBinOp "/" leftType TypeInt rightType TypeInt TypeInt
"%" -> typeCheckBinOp "%" leftType TypeInt rightType TypeInt TypeInt
"|" -> typeCheckBinOp "|" leftType TypeInt rightType TypeInt TypeInt
"&" -> typeCheckBinOp "&" leftType TypeInt rightType TypeInt TypeInt
"^" -> typeCheckBinOp "^" leftType TypeInt rightType TypeInt TypeInt
"&^" -> typeCheckBinOp "&^" leftType TypeInt rightType TypeInt TypeInt
"<<" -> typeCheckBinOp "<<" leftType TypeInt rightType TypeInt TypeInt
">>" -> typeCheckBinOp ">>" leftType TypeInt rightType TypeInt TypeInt
"<" -> typeCheckBinOp "<" leftType TypeInt rightType TypeInt TypeBool
">" -> typeCheckBinOp ">" leftType TypeInt rightType TypeInt TypeBool
"<=" -> typeCheckBinOp "<=" leftType TypeInt rightType TypeInt TypeBool
">=" -> typeCheckBinOp ">=" leftType TypeInt rightType TypeInt TypeBool
"||" -> typeCheckBinOp "||" leftType TypeBool rightType TypeBool TypeBool
"&&" -> typeCheckBinOp "&&" leftType TypeBool rightType TypeBool TypeBool
"==" -> typeCheckEqOp "==" leftType rightType
"!=" -> typeCheckEqOp "!=" leftType rightType
typeCheckBinOp :: String -> Type -> Type -> Type -> Type -> Type -> Compiler Type
typeCheckBinOp op actLeft expLeft actRight expRight retType = do
when (actLeft /= expLeft) $ typeError $ "Left argument of " ++ op ++ " expected " ++ show expLeft ++ " but found " ++ show actLeft ++ "."
when (actRight /= expRight) $ typeError $ "Right argument of " ++ op ++ " expected " ++ show expRight ++ " but found " ++ show actRight ++ "."
return retType
typeCheckEqOp :: String -> Type -> Type -> Compiler Type
typeCheckEqOp op left right = do
ass <- assignable left right
when (not ass) $ typeError $ "Arguments to " ++ op ++ " have mismatched types:\n\tLeft: " ++ show left ++ "\n\tRight: " ++ show right
return TypeBool
-- returns the size in words of a given type.
-- int, uint, bool and char are all 1 word. pointers, including strings and arrays, are also 1 word.
-- structs are the sum of the sizes of their fields
typeSizeInternal :: SymbolTable -> Type -> Int
typeSizeInternal _ TypeBool = 1
typeSizeInternal _ TypeInt = 1
typeSizeInternal _ TypeUint = 1
typeSizeInternal _ TypeChar = 1
typeSizeInternal _ TypeString = 1
typeSizeInternal _ (TypePointer _) = 1
typeSizeInternal _ (TypeArray _) = 1
typeSizeInternal _ TypeVoid = error "Void type has no size."
typeSizeInternal syms (TypeStruct fields) = sum $ map (typeSizeInternal syms . snd) fields
typeSizeInternal syms (TypeName s) = case M.lookup s syms of
Just t -> typeSizeInternal syms t
Nothing -> error $ "Cannot resolve type name " ++ show s
typeSize :: Type -> Compiler Int
typeSize t = do
ts <- gets types
return $ typeSizeInternal ts t
fieldOffset :: [(String, Type)] -> String -> Compiler Int
fieldOffset fields name = fieldOffset' fields name 0
where fieldOffset' [] name _ = compileError $ "No field named '" ++ name ++ "' found on struct."
fieldOffset' ((s, t):rest) name offset
| s == name = return offset
| otherwise = do
size <- typeSize t
fieldOffset' rest name (offset+size)
isPointer :: Type -> Bool
isPointer (TypePointer _) = True
isPointer _ = False
isArray :: Type -> Bool
isArray (TypeArray _) = True
isArray TypeString = True
isArray _ = False
-- return True if the first type can be converted into the second.
-- a value x of type V is convertible to a type T in any of the following cases:
-- * V is assignable to T
-- * V and T have the same underlying type
-- * V and T are (unnamed) pointer types, and their pointer base types are convertible
-- * V is []char and T is string
-- * V is string and T is []char
--
-- But we need to be more flexible here than in the original spec, to allow better assembly integration. So we allow:
-- * V is integral and T is any pointer or array type
convertible :: Type -> Type -> Compiler Bool
convertible l r = do
assign <- assignable l r
uL <- underlyingType l
uR <- underlyingType r
case (assign, uL == uR, l, r) of
(True, _, _, _) -> return True -- assignable
(False, True, _, _) -> return True -- identical underlying types
(_, _, TypePointer p1, TypePointer p2) -> convertible p1 p2
--(_, _, TypeInt, TypeString) -> return True -- we don't support this one.
(_, _, TypeArray TypeChar, TypeString) -> return True
(_, _, TypePointer TypeChar, TypeString) -> return True
(_, _, TypeString, TypeArray TypeChar) -> return True
(_, _, TypeString, TypePointer TypeChar) -> return True
-- off-spec flexible integral->pointer/array casting.
(_, _, TypeUint, TypePointer _) -> return True
(_, _, TypeName (QualIdent Nothing "uint"), TypePointer _) -> return True
(_, _, TypeUint, TypeArray _) -> return True
(_, _, TypeName (QualIdent Nothing "uint"), TypeArray _) -> return True
(_, _, TypeInt, TypePointer _) -> return True
(_, _, TypeName (QualIdent Nothing "int"), TypePointer _) -> return True
(_, _, TypeInt, TypeArray _) -> return True
(_, _, TypeName (QualIdent Nothing "int"), TypeArray _) -> return True
_ -> return False
-- returns True if a value of the first type can be assigned to a variable of the second type
-- a value x of type V is assignable to a type T in any of these cases:
-- * V is identical to T
-- * V and T have identical underlying types and at least one of V or T is not a named type
-- * x is nil and T is a pointer, function, slice, map, channel or interface type.
assignable :: Type -> Type -> Compiler Bool
assignable from to
| from == to = return True -- identical types, case 1.
| otherwise = do
uFrom <- underlyingType from
uTo <- underlyingType to
case (uFrom == uTo, from, to) of
(True, TypeName _, TypeName _) -> return False -- at least one must be non-named
(True, _, _) -> return True -- at least one is non-named and their base types are identical.
_ -> case (uFrom, uTo) of
(TypeInt, TypeUint) -> return True
(TypeUint, TypeInt) -> return True
(TypeNil, TypePointer _) -> return True
(TypeString, TypePointer TypeChar) -> return True
(TypePointer TypeChar, TypeString) -> return True
(TypeArray TypeChar, TypeString) -> return True
(TypeString, TypeArray TypeChar) -> return True
_ -> return False
underlyingType :: Type -> Compiler Type
underlyingType TypeString = return $ TypeArray TypeChar
underlyingType (TypeName t) = lookupType t >>= underlyingType
underlyingType (TypePointer TypeChar) = return $ TypeArray TypeChar
underlyingType (TypePointer t) = TypePointer <$> underlyingType t
underlyingType (TypeArray t) = TypeArray <$> underlyingType t
underlyingType t = return t
-- look up a type in the compiler environment by name.
lookupType :: QualIdent -> Compiler Type
lookupType i = do
ts <- gets types
case M.lookup i ts of
Nothing -> typeError $ "Unknown named type: " ++ show i
Just t -> return t
-- Compiles a Statement into a string of assembly code to perform it.
-- Should not add symbols to the table on a TypeDecl or VarDecl. They should have been added by the scan performed when beginning a block/file.
-- Initializers for variables should still be compiled in place (because they might depend on the values of other variables).
compile :: Statement -> Compiler [Asm]
compile (StmtTypeDecl name t) = return [] -- nothing to compile for typedecls.
-- note that this doesn't add the symbol, but rather expects it to already exist. this does compile initializers, though.
compile (StmtVarDecl name t Nothing) = addSymbol name t >> return [] -- nothing to do for a plain declaration
compile (StmtVarDecl name t (Just x)) = addSymbol name t >> setVar name x
compile (StmtConstDecl name t _) = addSymbol name t >> return [] -- nothing to do in code for constants.
compile (StmtShortVarDecl name x) = do
t <- typeCheck x
addSymbol name t
setVar name x
compile (StmtFunction _ _ _ Nothing) = return []
compile (StmtFunction name args ret (Just body)) = do
-- so a function. we need a new scope pushed, as well as a new set of locations for local variables and arguments.
-- here's how the base pointer is handled. we use J as a base pointer. it points at the first local.
-- the locals are at J+0, J+1, J+2, etc. the old value of J is stored at J+n, and any stack-passed args are in J+n+1, J+n+2, ...
-- first, harvest the locals from the function body.
addSymbol name (TypeFunction (map snd args) ret) -- add the function to the symbol table before we grab the state, to allow recursion.
s <- get
let allLocals = findLocals body -- [(QualIdent, Type)]
localCount = length allLocals
argLocations = [LocReg "A", LocReg "B", LocReg "C"] ++ map LocStack [localCount+2..] -- they're deeper than the locals, and the first three are in registers. +1 because the old PC return address is stored on top of them, and the base pointer is stored on top of that.
myArgs = zip (map (QualIdent Nothing . fst) args) argLocations -- [(QualIdent, Location)], as necessary
myLocalLocations = zip allLocals $ map LocStack [0..]
mySymbols = M.fromList $ map (first (QualIdent Nothing)) args -- locals are not included in the symbol table, they'll be added as their definitions pass by.
prefix <- mkLabel name
let s' = s {
locals = myLocalLocations,
args = myArgs,
dirtyRegs = [],
freeRegs = drop (min 3 (length args)) ["A", "B", "C", "X", "Y", "Z", "I"],
symbols = mySymbols : symbols s,
this = prefix }
put s'
bodyCode <- concat <$> mapM compile body
s'' <- get
-- add preamble and postamble, saving and restoring the dirty registers and quitting.
let preambleCode = [LabelDef prefix,
SET PUSH (Reg "J"), -- store the old value of J, the base pointer.
SUB SP (Lit localCount), -- make room for the locals
SET (Reg "J") SP] ++ -- and set J to be the new base pointer, pointing at the first local.
map (SET PUSH . Reg) (dirtyRegs s'')
postambleCode = [LabelDef (prefix ++ "_done")] ++
map (\r -> SET (Reg r) POP) (reverse (dirtyRegs s'')) ++
[ADD SP (Lit localCount), -- remove the locals
SET (Reg "J") POP, -- restore the old base pointer.
SET PC POP] -- and return
put s -- restore the original state, removing my locals, args and symbols.
return $ preambleCode ++ bodyCode ++ postambleCode
compile (StmtLabel s) = return [LabelDef s] -- this isn't easy to do re: labeled jumps and breaks and crap. maybe make this a container instead of an ordinary statement.
compile (StmtExpr x@(Call _ _)) = do
r <- getReg
code <- compileExpr x r
freeReg r
return code
compile (StmtExpr x@(BuiltinCall _ _ _)) = do
r <- getReg
code <- compileExpr x r
freeReg r
return code
compile (StmtExpr x) = error $ "Suspicious code. Expression " ++ show x ++ " has no side effects."
compile (StmtInc x) = compileIncDec ADD x
compile (StmtDec x) = compileIncDec SUB x
compile (StmtAssignment Nothing lvalue rvalue) = do
when (not $ isLvalue lvalue) $ compileError $ "Attempt to assign to non-lvalue " ++ show lvalue
lt <- typeCheck lvalue
rt <- typeCheck rvalue
assign <- assignable rt lt
when (not assign) $ typeError $ "Right side of assignment is not assignable to left side.\n\tLeft: " ++ show lt ++ "\n\tRight: " ++ show rt
-- if we get down here, then this assignment is legal, so compile it.
r <- getReg
exprCode <- compileExpr rvalue r
code <- case lvalue of
(Var i) -> do
locCode <- lookupLocation i >>= compileLocation
return $ exprCode ++ [SET locCode (Reg r)]
(Index x i) -> do
rx <- getReg
xCode <- compileExpr x rx
ri <- getReg
iCode <- compileExpr i ri
freeReg rx
freeReg ri
(TypeArray elementType) <- underlyingType =<< typeCheck x
size <- typeSize elementType
return $ exprCode ++ [MUL (Reg ri) (Lit size), ADD (Reg rx) (Reg ri), SET (AddrReg rx) (Reg r)]
(Selector x s) -> do
t <- typeCheck x
ut <- underlyingType t
fields <- case ut of
(TypeStruct fields) -> return fields
(TypePointer (TypeStruct fields)) -> return fields
_ -> typeError $ "Attempt to select a field from a non-struct value of type " ++ show t
offset <- fieldOffset fields s
rx <- getReg
xCode <- compileExpr x rx
freeReg rx
return $ exprCode ++ xCode ++ [ADD (Reg rx) (Lit offset), SET (AddrReg rx) (Reg r)]
_ -> error "Not implemented: Assigning to lvalues other than variables, array elements or struct fields."
freeReg r
return code
-- TODO: Optimization: some ops, like += and -=, can be optimized by using ADD instead of computing and setting. Priority low, though, only a couple of instructions wasted.
compile (StmtAssignment (Just op) lvalue rvalue) = compile (StmtAssignment Nothing lvalue (BinOp (LOp op) lvalue rvalue))
compile (StmtIf initializer condition ifbody elsebody) = do
-- the anatomy of an if-statement:
-- it begins with the initializer. then the condition is computed. a jump is compiled that if the condition is false jumps to the beginning of the else block or the end
-- at the end of the body a jump to the end is computed to skip over the else block.
-- or is it easier to go in reverse?
-- push the new scope before compiling the initializer, since any variables it defines are scoped in the if.
modify $ \s -> s { symbols = M.empty : symbols s }
initCode <- concat <$> mapM compile initializer
ct <- typeCheck condition
when (ct /= TypeBool) $ typeError $ "Condition of an if statement must have type bool, found " ++ show ct
r <- getReg
condCode <- compileExpr condition r
-- get a label prefix for this if, set 'this' appropriately.
prefix <- uniqueLabel
let jumpCode = [IFE (Reg r) (Lit 0),
SET PC (Label $ if null elsebody then prefix ++ "_endif" else prefix ++ "_else")] -- jump if the condition is false, since we're jumping to the else block.
freeReg r -- don't need this reserved anymore, because the jump is now compiled.
ifCode <- concat <$> mapM compile ifbody
let ifJumpCode = if null elsebody then [] else [SET PC (Label (prefix ++ "_endif"))] -- add a jump to the end of the if body if there's an else to jump over.
elseCode <- case elsebody of
[] -> return []
_ -> do
elseCode <- concat <$> mapM compile elsebody
return $ [LabelDef (prefix ++ "_else")] ++ elseCode
-- TODO I think variables declared in any part of the if are spreading to the rest of it. Mostly harmless.
-- remove the scope
modify $ \s -> s { symbols = tail (symbols s) }
return $ initCode ++ condCode ++ jumpCode ++ ifCode ++ ifJumpCode ++ elseCode ++ [LabelDef (prefix ++ "_endif")]
compile (StmtFor initializer condition incrementer body) = do
prefix <- uniqueLabel
-- add the new scope before the initializer
modify $ \s -> s { symbols = M.empty : symbols s }
initCode <- concat <$> mapM compile initializer
let topLabelCode = [LabelDef (prefix ++ "_top")]
-- have to typecheck after the initializer is compiled, or a condition that references a newly initialized variable will error.
ct <- typeCheck condition
when (ct /= TypeBool) $ typeError $ "Loop condition must have type bool, found " ++ show ct
r <- getReg
condCode <- compileExpr condition r
let condCheckCode = [IFE (Reg r) (Lit 0), SET PC (Label (prefix ++ "_end"))]
freeReg r
bodyCode <- concat <$> mapM compile body
incCode <- concat <$> mapM compile incrementer
let topJumpCode = [SET PC (Label (prefix ++ "_top"))]
let endLabelCode = [LabelDef (prefix ++ "_end")]
modify $ \s -> s { symbols = tail (symbols s) } -- remove the new loop scope.
return $ initCode ++ topLabelCode ++ condCode ++ condCheckCode ++ bodyCode ++ incCode ++ topJumpCode ++ endLabelCode
compile (StmtReturn mx) = do
exprCode <- case mx of
Nothing -> return []
Just x -> do
r <- getReg
code <- compileExpr x r
freeReg r
return $ code ++ [SET (Reg "A") (Reg r)]
t <- gets this
return $ exprCode ++ [SET PC (Label (t ++ "_done"))]
compile (StmtGoto label) = return [SET PC (Label label)]
compile s = compileError $ "Not implemented: " ++ show s
compileIncDec :: (Arg -> Arg -> Asm) -> Expr -> Compiler [Asm]
compileIncDec opcode (Var i) = do
xt <- typeCheck (Var i)
xut <- underlyingType xt
case xut of
TypeInt -> do
locCode <- lookupLocation i >>= compileLocation
return [opcode locCode (Lit 1)]
_ -> typeError $ "Attempt to ++ or -- non-int type " ++ show xt
-- turns a Location into the assembly string representing it
compileLocation :: Location -> Compiler Arg
compileLocation (LocReg r) = return $ Reg r
compileLocation (LocStack n) = return $ AddrRegLit "J" n -- can't use PICK or SP, unknown levels of saved values piled on top. Use J, the frame pointer.
compileLocation (LocLabel s) = return $ AddrLabel s
compileLocation (LocConstant i) = return $ Lit i
-- compiles an expression, storing the result in the given register.
compileExpr :: Expr -> String -> Compiler [Asm]
compileExpr (LitInt n) r = return [SET (Reg r) (Lit n)]
compileExpr (LitBool b) r = return [SET (Reg r) (Lit (if b then 1 else 0))]
compileExpr (LitChar c) r = return [SET (Reg r) (Lit (ord c))]
compileExpr (LitString s) r = do
unique <- uniqueLabel
modify $ \st -> st { strings = (unique, s) : strings st }
return [SET (Reg r) (Label unique)]
compileExpr (LitComposite _ _) _ = error "Composite literals not implemented"
compileExpr (Var (QualIdent Nothing "nil")) r = return [SET (Reg r) (Lit 0)]
compileExpr (Var i) r = do
loc <- lookupLocation i >>= compileLocation
return [SET (Reg r) loc]
compileExpr (Selector x s) r = do
t <- typeCheck x
ut <- underlyingType t
fields <- case ut of
(TypeStruct fields) -> return fields
(TypePointer (TypeStruct fields)) -> return fields
_ -> typeError $ "Attempt to select field '" ++ s ++ "' from non-struct type " ++ show t
xCode <- compileExpr x r
offset <- fieldOffset fields s
return $ xCode ++ [ADD (Reg r) (Lit offset), SET (Reg r) (AddrReg r)]
compileExpr (Index arr ix) r = do
arrType <- typeCheck arr
size <- typeSize arrType
arrCode <- compileExpr arr r
ixR <- getReg
ixCode <- compileExpr ix ixR
freeReg ixR
return $ arrCode ++ ixCode ++
(if size > 1 then [MUL (Reg ixR) (Lit size)] else []) ++ -- if the size of the array elements is > 1, multiply from the index to the offset. TODO optimization trivial: shift for sizes that are powers of 2.
[ADD (Reg r) (Reg ixR), SET (Reg r) (AddrReg r)]
compileExpr (Call f args) r = do
when (length args >= 4) $ error "Functions with more than 3 arguments are not implemented."
-- I don't need the type, but I do need to typecheck.
t <- typeCheck (Call f args)
tf <- typeCheck f
(saveCode, restoreCode) <- saveRegsForCall r
ra <- getReg
argCode <- concatMap (++ [SET PUSH (Reg ra)]) <$> sequence (map (flip compileExpr ra) args)
let popArgsCode = reverse $ zipWith (\r _ -> SET (Reg r) POP) ["A", "B", "C"] args
freeReg ra
jsrCode <- case (tf,f) of
(TypeFunction _ _, Var name) -> do
label <- mkLabel name
return [JSR (Label label)]
(TypePointer (TypeFunction _ _), x) -> do
pr <- getReg
ptrCode <- compileExpr x pr
freeReg pr
return $ ptrCode ++ [JSR (Reg pr)]
(t_, f_) -> error $ "Bad case: t = " ++ show t_ ++ ", f = " ++ show f_
let returnCode = [SET (Reg r) (Reg "A")]
return $ saveCode ++ argCode ++ popArgsCode ++ jsrCode ++ returnCode ++ restoreCode
-- creating an array with a length
compileExpr (BuiltinCall LNew (Just (TypeArray t)) (n:_)) r = do
(saveCode, restoreCode) <- saveRegsForCall r
size <- typeSize t
lengthCode <- compileExpr n r
return $ saveCode ++ lengthCode ++
[SET (Reg "A") (Lit size),
MUL (Reg "A") (Reg r), -- size in words
JSR (AddrLit 9)] ++ -- pointer is stored in A
(if r /= "A" then [SET (Reg r) (Reg "A")] else []) ++
restoreCode
compileExpr (BuiltinCall LNew (Just t) _) r = do