Compare commits
8 Commits
3d17813eb4
...
main
Author | SHA1 | Date | |
---|---|---|---|
1d91d7a39d
|
|||
bd7f614d23
|
|||
f4906899ee
|
|||
d66f1e1401
|
|||
c47f713a0d
|
|||
c1fd18d525
|
|||
fd16a12e7c
|
|||
b013ba0e55
|
16
README.md
16
README.md
@ -1,10 +1,26 @@
|
||||
# Windows12
|
||||
A C-like compiled programming language implemented in Haskell.
|
||||
|
||||
## Examples
|
||||
Example programs can be found in the `test` directory.
|
||||
|
||||
## Usage
|
||||
Note: You must have version 9.0 of the Haskell compiler GHC installed. Otherwise
|
||||
Cabal will not be able to obtain the necessary dependencies.
|
||||
|
||||
It is recommended to use the provided `shell.nix` file to create the proper
|
||||
environment. Alternatively, you can run the project from the supplied
|
||||
binaries in the [releases](https://gitea.mregirouard.com/eta357/Windows-12-Compiler/releases)
|
||||
section.
|
||||
|
||||
```shell
|
||||
cabal run windows12 <input file> out.ll
|
||||
llc out.ll -o out.s
|
||||
gcc out.s -o out
|
||||
./out
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
```shell
|
||||
cabal run windows12-qc
|
||||
```
|
||||
|
94
src/QuickCheckTests.hs
Normal file
94
src/QuickCheckTests.hs
Normal file
@ -0,0 +1,94 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module Main where
|
||||
|
||||
import Test.QuickCheck
|
||||
|
||||
import Windows12.Ast
|
||||
import Windows12.Parser (programP)
|
||||
import Text.Megaparsec (parse)
|
||||
|
||||
import Data.String.Conversions (cs)
|
||||
|
||||
import Data.Text
|
||||
import Data.Text.Internal
|
||||
import Data.Text.Prettyprint.Doc (pretty)
|
||||
|
||||
main :: IO ()
|
||||
main = quickCheck prop_print_parse
|
||||
|
||||
-- Ensure that pretty-printing and parsing are inverses
|
||||
prop_print_parse :: Program -> Bool
|
||||
prop_print_parse p = Right p == (parse programP "" (cs (show (pretty p))))
|
||||
|
||||
instance Arbitrary Data.Text.Internal.Text where
|
||||
arbitrary = listOf1 (elements ['a'..'z']) >>= return . Data.Text.pack
|
||||
|
||||
instance Arbitrary BinOp where
|
||||
arbitrary = elements [Add, Sub, Mul, Div, Mod, Eq, Ne, Lt, Gt, Le, Ge, And, Or,
|
||||
BitAnd, BitOr, BitXor, ShiftL, ShiftR]
|
||||
|
||||
instance Arbitrary UnOp where
|
||||
arbitrary = elements [Neg, Not, BitNot]
|
||||
|
||||
instance Arbitrary AssignOp where
|
||||
arbitrary = elements [BaseAssign, AddAssign, SubAssign, MulAssign, DivAssign,
|
||||
ModAssign, BitAndAssign, BitOrAssign, BitXorAssign, ShiftLAssign, ShiftRAssign]
|
||||
|
||||
instance Arbitrary Expr where
|
||||
arbitrary = oneof
|
||||
[ Id <$> arbitrary
|
||||
, IntLit <$> arbitrary
|
||||
, UIntLit <$> arbitrary
|
||||
, FloatLit <$> arbitrary
|
||||
, StrLit <$> arbitrary
|
||||
, BoolLit <$> arbitrary
|
||||
, CharLit <$> arbitrary
|
||||
, BinOp <$> arbitrary <*> arbitrary <*> arbitrary
|
||||
, UnOp <$> arbitrary <*> arbitrary
|
||||
, Call <$> arbitrary <*> arbitrary
|
||||
, Index <$> arbitrary <*> arbitrary
|
||||
, Member <$> arbitrary <*> arbitrary
|
||||
, Cast <$> arbitrary <*> arbitrary
|
||||
, Sizeof <$> arbitrary
|
||||
, StructInit <$> arbitrary <*> arbitrary
|
||||
]
|
||||
|
||||
instance Arbitrary Stmt where
|
||||
arbitrary = oneof
|
||||
[ Expr <$> arbitrary
|
||||
, Return <$> arbitrary
|
||||
, If <$> arbitrary <*> arbitrary <*> arbitrary
|
||||
, While <$> arbitrary <*> arbitrary
|
||||
, Assign <$> arbitrary <*> arbitrary <*> arbitrary
|
||||
, Block <$> arbitrary
|
||||
, Var <$> arbitrary <*> arbitrary <*> arbitrary
|
||||
]
|
||||
|
||||
-- Massively simplified types: No pointers, void, structs, enums
|
||||
instance Arbitrary Type where
|
||||
arbitrary = elements [IntType, UIntType, FloatType, StrType, BoolType,
|
||||
CharType, PtrType IntType, ArrayType IntType]
|
||||
|
||||
instance Arbitrary Bind where
|
||||
arbitrary = Bind <$> arbitrary <*> arbitrary
|
||||
|
||||
instance Arbitrary TLStruct where
|
||||
arbitrary = Struct <$> arbitrary <*> arbitrary
|
||||
|
||||
-- ensure the enum has at least one field
|
||||
instance Arbitrary TLEnum where
|
||||
arbitrary = Enum <$> arbitrary <*> listOf1 arbitrary
|
||||
|
||||
instance Arbitrary TLFunc where
|
||||
arbitrary = Func <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
|
||||
|
||||
instance Arbitrary TL where
|
||||
arbitrary = oneof
|
||||
[ TLStruct <$> arbitrary
|
||||
, TLEnum <$> arbitrary
|
||||
, TLFunc <$> arbitrary
|
||||
]
|
||||
|
||||
instance Arbitrary Program where
|
||||
arbitrary = Program <$> arbitrary <*> arbitrary <*> arbitrary
|
@ -192,14 +192,14 @@ instance Pretty Bind where
|
||||
pretty (Bind n t) = pretty n <+> ":" <+> pretty t
|
||||
|
||||
instance Pretty TLStruct where
|
||||
pretty (Struct n fields) = "struct" <+> pretty n <+> prettyBlock fields
|
||||
pretty (Struct n fields) = "struct" <+> pretty n <+> prettyFields fields
|
||||
|
||||
instance Pretty TLEnum where
|
||||
pretty (Enum n fields) = "enum" <+> pretty n <+> prettyBlock fields
|
||||
pretty (Enum n fields) = "enum" <+> pretty n <+> prettyFields fields
|
||||
|
||||
instance Pretty TLFunc where
|
||||
pretty (Func n args ret body) =
|
||||
pretty n <> parens (hsep (punctuate comma (map pretty args))) <+> "->" <+> pretty ret <+> prettyBlock body
|
||||
"fn " <> pretty n <> parens (hsep (punctuate comma (map pretty args))) <+> "->" <+> pretty ret <+> prettyBlock body
|
||||
|
||||
instance Pretty TL where
|
||||
pretty (TLStruct s) = pretty s
|
||||
@ -209,5 +209,8 @@ instance Pretty TL where
|
||||
instance Pretty Program where
|
||||
pretty (Program structs enums funcs) = vsep (map pretty structs) <> line <> vsep (map pretty enums) <> line <> vsep (map pretty funcs)
|
||||
|
||||
prettyFields :: (Pretty a) => [a] -> Doc ann
|
||||
prettyFields fields = lbrace <> line <> indent 4 (vsep (punctuate comma (map pretty fields))) <> line <> rbrace
|
||||
|
||||
prettyBlock :: (Pretty a) => [a] -> Doc ann
|
||||
prettyBlock stmts = lbrace <> line <> indent 4 (vsep (map pretty stmts)) <> line <> rbrace
|
||||
|
@ -265,23 +265,18 @@ codegenStmt (TWhile cond body) = mdo
|
||||
end <- block `named` "end"
|
||||
return ()
|
||||
|
||||
codegenStmt (TAssign BaseAssign l@(t, (TId name)) e) = do
|
||||
codegenStmt (TAssign BaseAssign l e) = do
|
||||
op <- codegenExpr e
|
||||
var <- codegenLVal l
|
||||
store var 0 op
|
||||
|
||||
codegenStmt (TAssign BaseAssign l@((StructType tName), (LTMember ((_, TId sName)) field)) e) = do
|
||||
op <- codegenExpr e
|
||||
struct <- codegenLVal l
|
||||
store struct 0 op
|
||||
|
||||
codegenStmt (TAssign AddAssign l@(t, (TId name)) e) = do
|
||||
codegenStmt (TAssign AddAssign l e) = do
|
||||
op <- codegenExpr e
|
||||
var <- codegenLVal l
|
||||
val <- load var 0
|
||||
store var 0 =<< add val op
|
||||
|
||||
codegenStmt (TAssign SubAssign l@(t, (TId name)) e) = do
|
||||
codegenStmt (TAssign SubAssign l e) = do
|
||||
op <- codegenExpr e
|
||||
var <- codegenLVal l
|
||||
val <- load var 0
|
||||
|
43
test/arith.w12
Normal file
43
test/arith.w12
Normal file
@ -0,0 +1,43 @@
|
||||
fn factorial(n: int) -> int {
|
||||
var result: int;
|
||||
|
||||
if n == 0 {
|
||||
result = 1;
|
||||
} else {
|
||||
result = n * factorial(n - 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn is_positive(n: int) -> bool {
|
||||
return n > 0;
|
||||
}
|
||||
|
||||
fn main() -> int {
|
||||
var a: int = 5;
|
||||
printf("a = %d\n", a);
|
||||
|
||||
var b: int = 3;
|
||||
printf("b = %d\n", b);
|
||||
|
||||
var c: int = (a + b) * 2 - 1;
|
||||
c += factorial(5);
|
||||
|
||||
printf("c = %d\n", c);
|
||||
|
||||
while c > 0 {
|
||||
printf("c = %d\n", c);
|
||||
|
||||
if c > 50 {
|
||||
printf("c is greater than 50\n");
|
||||
} else {
|
||||
printf("c is less than or equal to 50\n");
|
||||
}
|
||||
|
||||
c -= 16;
|
||||
}
|
||||
|
||||
printf("End result: %d\n", c);
|
||||
return 0;
|
||||
}
|
1
test/bad.w12
Normal file
1
test/bad.w12
Normal file
@ -0,0 +1 @@
|
||||
function test() {}
|
21
test/basic-structs.w12
Normal file
21
test/basic-structs.w12
Normal file
@ -0,0 +1,21 @@
|
||||
struct Test {
|
||||
a: int,
|
||||
b: char,
|
||||
}
|
||||
|
||||
fn main() -> int {
|
||||
var t: Test;
|
||||
t.a = 5;
|
||||
t.b = 'a';
|
||||
|
||||
printf("t.a = %d\n", t.a);
|
||||
printf("t.b = %c\n", t.b);
|
||||
|
||||
t.a += 3;
|
||||
t.b = 'b';
|
||||
|
||||
printf("t.a = %d\n", t.a);
|
||||
printf("t.b = %c\n", t.b);
|
||||
|
||||
return 0;
|
||||
}
|
0
test/empty.w12
Normal file
0
test/empty.w12
Normal file
38
test/fib.w12
Normal file
38
test/fib.w12
Normal file
@ -0,0 +1,38 @@
|
||||
fn rec_fib(n: int) -> int {
|
||||
if n <= 1 {
|
||||
return n;
|
||||
}
|
||||
|
||||
return rec_fib(n - 1) + rec_fib(n - 2);
|
||||
}
|
||||
|
||||
fn loop_fib(n: int) -> int {
|
||||
var a: int = 0;
|
||||
var b: int = 1;
|
||||
var i: int = 0;
|
||||
|
||||
while i < n {
|
||||
var c: int = a + b;
|
||||
a = b;
|
||||
b = c;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
fn main() -> int {
|
||||
var n: int = 20;
|
||||
var rec_result: int = rec_fib(n);
|
||||
var loop_result: int = loop_fib(n);
|
||||
printf("Fibonacci of %d via recursion is %d\n", n, rec_result);
|
||||
printf("Fibonacci of %d via loop is %d\n", n, loop_result);
|
||||
|
||||
if rec_result == loop_result {
|
||||
printf("Results match\n");
|
||||
} else {
|
||||
printf("Results do not match\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
32
test/functions.w12
Normal file
32
test/functions.w12
Normal file
@ -0,0 +1,32 @@
|
||||
fn mult3(a: int, b: int, c: int) -> int {
|
||||
return a * b * c;
|
||||
}
|
||||
|
||||
fn loop_test() -> int {
|
||||
var i: int = 0;
|
||||
var result: int = 0;
|
||||
|
||||
while i < 10 {
|
||||
printf("i = %d\n", i);
|
||||
result += i;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn main() -> int {
|
||||
var a: int = 5;
|
||||
var b: int = 3;
|
||||
var c: int = 2;
|
||||
|
||||
var result: int = mult3(a, b, c);
|
||||
printf("Result: %d\n", result);
|
||||
|
||||
result = loop_test();
|
||||
printf("Result: %d\n", result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -92,3 +92,36 @@ executable windows12
|
||||
|
||||
-- Base language which the package is written in.
|
||||
default-language: Haskell2010
|
||||
|
||||
executable windows12-qc
|
||||
-- Import common warning flags.
|
||||
import: warnings
|
||||
|
||||
-- .hs or .lhs file containing the Main module.
|
||||
main-is: QuickCheckTests.hs
|
||||
|
||||
-- Modules included in this executable, other than Main.
|
||||
other-modules:
|
||||
Windows12.Ast
|
||||
Windows12.Lexer
|
||||
Windows12.Parser
|
||||
|
||||
-- LANGUAGE extensions used by modules in this package.
|
||||
-- other-extensions:
|
||||
|
||||
-- Other library packages from which modules are imported.
|
||||
build-depends:
|
||||
base >= 4.15.1 && < 4.16,
|
||||
megaparsec >= 9.6.1 && < 9.7,
|
||||
text >= 1.2.5 && < 1.3,
|
||||
parser-combinators >= 1.3.0 && < 1.4,
|
||||
prettyprinter >= 1.5.1 && < 1.6,
|
||||
string-conversions >= 0.4.0 && < 0.5,
|
||||
mtl >= 2.2.2 && < 2.3,
|
||||
QuickCheck >= 2.14.2 && < 2.15,
|
||||
|
||||
-- Directories containing source files.
|
||||
hs-source-dirs: src
|
||||
|
||||
-- Base language which the package is written in.
|
||||
default-language: Haskell2010
|
||||
|
Reference in New Issue
Block a user