commit f23a84e4090025da26b8545345934f8378d15964 Author: channa Date: Fri Jan 19 21:43:42 2024 +0100 New project diff --git a/META-INF/RASCAL.MF b/META-INF/RASCAL.MF new file mode 100644 index 0000000..2089e8e --- /dev/null +++ b/META-INF/RASCAL.MF @@ -0,0 +1,5 @@ +Manifest-Version: 0.0.1 +Project-Name: minipas +Source: src/main/rascal +Require-Libraries: + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..64f12d9 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# A compiler for MiniPas using Rascal + +* In my compiler construction subject, we made a compiler for a language called + `MiniPas` (a subset of `Pascal`) in C. It generates LLIR code from a `.pas` + file. + +## Grammar + +* The grammar of the langauge can be found in `src/Syntax.rsc`. We make some + comments here. +* Since `MiniPas` has notions of both functions and procedures, we refer to either + as a `SubProg`. +* Keywords are case insensitive but identifiers are case sensitive. + +## Acknowledgements +* The grammar of `MiniPas` is from the course `Compiler Construction` in the University of Groningen. + The programs in `test/input` for `MiniPas` are from this course as well. +* The structure of the `Rascal` code comes from the course `Software Language Engineering` in the University of Groningen \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..162f233 --- /dev/null +++ b/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + + org.rascalmpl + minipas + 0.1.0-SNAPSHOT + + + UTF-8 + + + + + usethesource + https://releases.usethesource.io/maven/ + + + + + + usethesource + https://releases.usethesource.io/maven/ + + + + + + org.rascalmpl + rascal + 0.34.1 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + -parameters + 11 + + + + org.rascalmpl + rascal-maven-plugin + 0.8.2 + + true + ${project.build.outputDirectory} + + ${project.basedir}/src/main/rascal + + + + + + + \ No newline at end of file diff --git a/src/main/rascal/AST.rsc b/src/main/rascal/AST.rsc new file mode 100644 index 0000000..e69de29 diff --git a/src/main/rascal/CST2AST.rsc b/src/main/rascal/CST2AST.rsc new file mode 100644 index 0000000..e69de29 diff --git a/src/main/rascal/Check.rsc b/src/main/rascal/Check.rsc new file mode 100644 index 0000000..e69de29 diff --git a/src/main/rascal/Syntax.rsc b/src/main/rascal/Syntax.rsc new file mode 100644 index 0000000..9b200e9 --- /dev/null +++ b/src/main/rascal/Syntax.rsc @@ -0,0 +1,55 @@ +module Syntax + +extend lang::std::Layout; +extend lang::std::Id; +extend lang::std::ASCII; + +start syntax Progarm = 'program' Id ";" ConstDecl* VarDecl* SubProgDecl* CompoundStatement; +syntax ConstDecl = 'const' Id "=" Number; +syntax IdList = Id {"," Id}*; +syntax TypeSpec = BasicType | 'array' "[" Number ".." Number "]" 'of' BasicType; +syntax BasicType = 'integer' | 'real'; +syntax SubProgDecl = 'function' Id Parameters ":" BasicType ";" VarDecl* CompoundStatement ";" | + 'procedure' Id Parameters? ";" VarDecl* CompoundStatement ";" + ; +syntax Parameters = "(" ParameterList ")"; +syntax ParameterList = 'var'? IdList ":" TypeSpec {";" ParameterList}*; +syntax CompoundStatement = 'begin' StatementList? 'end'; +syntax StatementList = Statement {";" Statement}*; +syntax Statement = Lhs ":=" Expr | + ProcedureCall | + CompoundStatement | + 'skip' | + 'if' Guard 'then' Statement 'else' Statement | + 'while' Guard 'do' Statement + ; + +syntax Guard = Expr Relop Expr | + 'not' Guard | + Guard 'or' Guard | + Guard 'and' GUard | + "(" Guard ")" + ; + +syntax LhsList = Lhs {"," Lhs}*; +syntax Lhs = Id ("[" ExprList "]")?; +syntax ProcedureCall = Id ("(" ExprList ")")? | + 'readln'"(" LhsList ")" | + 'writeln'"(" ExprList ")" + ; +syntax ExprList = Expr {"," Expr}*; +syntax Expr = Id ("[" Expr (".." Expr)?"]")? | + Num | + Id "(" ExprList ")" | + Expr "+" Expr | + Expr "-" Expr | + Expr "*" Expr | + Expr "/" Expr | + Expr 'div' Expr | + Expr 'mod' Expr | + "-" Expr | + "(" Expr ")" + ; + +lexical Relop = "\<"|"\<="|"\>"|"\>="|"=="|"!="; +lexical Number = Digit* ("." Digit+)?; \ No newline at end of file diff --git a/src/main/rascal/main.rsc b/src/main/rascal/main.rsc new file mode 100644 index 0000000..c389794 --- /dev/null +++ b/src/main/rascal/main.rsc @@ -0,0 +1,18 @@ +module MyTest + +import ParseTree; + +void main() { + [loc] files = getFilesIn(|project://test/input|); + for (loc file <- files) { + runTestOn(file); + } +} + +void runTestOn(loc file) { + println(file); +} + +void getFilesIn(loc dir) { + return |project://test/input/arr.pas|; +} \ No newline at end of file diff --git a/test/input/arr.pas b/test/input/arr.pas new file mode 100644 index 0000000..c6aa23f --- /dev/null +++ b/test/input/arr.pas @@ -0,0 +1,24 @@ +{ This program does some work on arrays, passing them by value and by reference and such } +program arr; + +const N = 5; + +var arr : array [5 .. 6] of real; + +function some (rra : array [1 .. 2] of real; y : real; z : real) : real; +begin + some := rra[1] + y + z +end; + +procedure some2 (var rra : array [2 .. 3] of real); +begin + rra[3] := rra[3] + 20 +end; + +begin + readln(arr[5], arr[6]); + writeln(some(arr, arr[5], N)); + writeln(some(arr, 5, 5.6)); + some2(arr); + writeln(arr[5], arr[6]) +end. \ No newline at end of file diff --git a/test/input/arrparmechanism.pas b/test/input/arrparmechanism.pas new file mode 100644 index 0000000..6a6e1d0 --- /dev/null +++ b/test/input/arrparmechanism.pas @@ -0,0 +1,32 @@ +{ Example from the lab pdf } +PROGRAM arrparmechanism; + +VAR a : ARRAY [0 .. 5] OF integer; {array with values a[0], a[1], a[2], a[3], a[4], a[5]} +VAR b : ARRAY [1 .. 6] OF integer; {array with values b[1], b[2], b[3], b[4], b[5], b[6]} + +PROCEDURE arrcopy(VAR dest : ARRAY [0 .. 5] OF integer; src : ARRAY [5 .. 10] OF integer); +{dest is a var-paremeter, src is a value-parameter. Note the funny array bounds!} +VAR i : integer; +BEGIN + i := 0 + ; WHILE i <> 6 DO + BEGIN + dest[i] := src[i+5] { reflected to caller: dest is a var-parameter!} + ; src[i+5] := 0 { not reflected to caller: src is a value-parameter!} + ; i := i + 1 + END +END; + +FUNCTION sum(a : ARRAY [1 .. 3] OF integer) : integer; +BEGIN + sum := a[1] + a[2] + a[3] +END; + +BEGIN + readln(b[1], b[2], b[3], b[4], b[5], b[6]) + ; arrcopy(a,b) {here index range translation takes place, the range sizes fit } + ; writeln(a[0],a[1],a[2],a[3],a[4],a[5]) {this will print: 1 2 3 4 5 6 } + ; writeln(b[1],b[2],b[3],b[4],b[5],b[6]) {this will also print: 1 2 3 4 5 6 } + ; writeln(sum(a[0..2])) { note the section notation: this will print 6 (because 1+2+3=6) } + ; writeln(sum(a[1..3])) { note the section notation: this will print 9 (because 2+3+4=9) } +END. \ No newline at end of file diff --git a/test/input/comparisons.pas b/test/input/comparisons.pas new file mode 100644 index 0000000..4a28a1e --- /dev/null +++ b/test/input/comparisons.pas @@ -0,0 +1,16 @@ +{ This program tries some logic statements } +program logic; + +var x, z : integer; +var xs : array [1 .. 10] of integer; +var y : real; +var ys : array [1 .. 10] of real; + +begin + readln(x, xs[1], y, ys[1]); + if x < 10 and xs[1] < 1 or not(y > 0 and ys[1] >= 3 and y < 2.3 and ys[1] < 3.3) then + z := 100 + else + z := 999; + writeln(z) +end. \ No newline at end of file diff --git a/test/input/expressions.pas b/test/input/expressions.pas new file mode 100644 index 0000000..2f268cb --- /dev/null +++ b/test/input/expressions.pas @@ -0,0 +1,33 @@ +program sign; + +const N = 4; +const M = 2.3; + +var x, y, xy, xyz : integer; +var z : real; +var as, cs : array [2 .. 2] of real; { Array of size 1 } +var bs : array [2 .. 2] of integer; + +begin + as[2] := 25.3; { assign real to *real* } + as[2] := 2; { assign int to *real* } + bs[2] := 1; + cs[2] := bs[2]; + as[2] := cs[2]; + x := N; + z := M; + z := 3 / 2; + z := 3.5 * 3 + 2 - 1; + z := 3.4 / 5; + z := 3.5 / 3.4 - 3.4; + y := 3 div 2; + z := bs[2]; + x := 42; + y := -1; + xy := x*y; + xyz := -1*x + (-xy); + x := -x - x; + z := as[2]; + writeln(z); + writeln(10 * 3 / 2 + 6 - 5 / 4 + (4 / 3 * 8 + 6 - 7) * 25 - 15 + 4) +end. diff --git a/test/input/fibonacci.pas b/test/input/fibonacci.pas new file mode 100644 index 0000000..929d6bf --- /dev/null +++ b/test/input/fibonacci.pas @@ -0,0 +1,52 @@ +program fibonacci; + +var n,i : integer; +var fibs : array [1 .. 35] of integer; + +function fib(n : integer) : integer; +begin + if n <= 2 then + fib := 1 + else + fib := fib(n-1) + fib(n-2) +end; + +function fibMem(n : integer) : integer; +var fibCache : array [1 .. 100] of integer; +var i : integer; +begin + if n > 100 then + fibMem := 0 + else + begin + fibCache[1] := 1; + fibCache[2] := 1; + i := 3; + while i <= n do + begin + fibCache[i] := fibCache[i - 1] + fibCache[i - 2]; + i := i + 1 + end; + fibMem := fibCache[i-1] + end +end; + +begin + readln(n); + i := 1; + while i <= n do + begin + writeln(fib(i)); + i := i + 1 + end; + { the dynamic } + writeln(fibMem(35)); + { now for the array } + i := 1; + while i <= 35 do + begin + fibs[i] := fib(i); + i := i + 1 + end; + writeln(fibs[35]) +end. diff --git a/test/input/gcd.pas b/test/input/gcd.pas new file mode 100644 index 0000000..7891cff --- /dev/null +++ b/test/input/gcd.pas @@ -0,0 +1,41 @@ +{ This program computes the greatest common divisor in three different ways } +PROGRAM euclid; + +VAR a,b : integer; + +function gcd_euclid(a, b : integer) : integer; +begin + while a <> b do + begin + while a > b do a := a - b; + while b > a do b := b - a + end; + gcd_euclid := a +end; + +FUNCTION gcd_recursive(u, v : integer) : integer; +BEGIN + IF u mod v <> 0 THEN + gcd_recursive := gcd_recursive(v, u mod v) + ELSE + gcd_recursive := v +END; + +function gcd_iterative(u, v : integer) : integer; +var t : integer; +begin + while v <> 0 do + begin + t := u; + u := v; + v := t mod v + end; + gcd_iterative := u +end; + +BEGIN + readln(a,b); + writeln(gcd_recursive(a,b)); + writeln(gcd_iterative(a,b)); + writeln(gcd_euclid(a,b)) +END. diff --git a/test/input/ipexam-alt.pas b/test/input/ipexam-alt.pas new file mode 100644 index 0000000..af5974b --- /dev/null +++ b/test/input/ipexam-alt.pas @@ -0,0 +1,12 @@ +{ Example program implementing some old IP assignment } +PROGRAM alt; +var shift : integer; +begin + readln(shift); + if shift < 0 then + shift := shift + shift + else + shift := shift - shift; + writeln(shift) +end +. diff --git a/test/input/ipexam-calc-arr.pas b/test/input/ipexam-calc-arr.pas new file mode 100644 index 0000000..b9ed69a --- /dev/null +++ b/test/input/ipexam-calc-arr.pas @@ -0,0 +1,63 @@ +{ Example program implementing an old IP assignment that uses arrays by reference } +PROGRAM calc; +{ + Small calculator with 4 registers + : integer [1..4] + : real + Options: + - 0 exit + - 1 : assign value to register + - 2 store (rb + rc) in ra (registers) + - 3 store (rb * rc) in ra (registers) +} + +var rt : real; +var r : array [1 .. 4] of real; +var option, rid1, rid2, rid3 : integer; + +function readRegister(var regs : array [1 .. 4] of real; regId : integer) : real; +begin + if regId >= 1 and regId <= 4 then + readRegister := regs[regId] + else + readRegister := 0 +end; + +procedure writeRegister(var regs : array [1 .. 4] of real; regId : integer; number : real); +begin + if regId >= 1 and regId <= 4 then + regs[regId] := number + else + skip +end; + +begin + r[1] := 0; + r[2] := 0; + r[3] := 0; + r[4] := 0; + writeln(r[1], r[2], r[3], r[4]); + readln(option); + while option <> 0 do + begin + if option <= 1 then + begin + readln(rid1, rt); + writeRegister(r, rid1, rt) + end + else if option <= 2 then + begin + readln(rid1, rid2, rid3); + writeRegister(r, rid1, readRegister(r, rid2) + readRegister(r, rid3)) + end + else if option <= 3 then + begin + readln(rid1, rid2, rid3); + writeRegister(r, rid1, readRegister(r, rid2) * readRegister(r, rid3)) + end + else skip; + writeln(r[1], r[2], r[3], r[4]); + readln(option) + end +end +. diff --git a/test/input/ipexam-calc.pas b/test/input/ipexam-calc.pas new file mode 100644 index 0000000..5429d3a --- /dev/null +++ b/test/input/ipexam-calc.pas @@ -0,0 +1,75 @@ +{ Example program implementing an old IP assignment that uses arrays by reference } +PROGRAM calc; +{ + Small calculator with 4 registers + : integer [1..4] + : real + Options: + - 0 exit + - 1 : assign value to register + - 2 store (rb + rc) in ra (registers) + - 3 store (rb * rc) in ra (registers) +} + +var r1, r2, r3, r4, rt: real; +var option, rid1, rid2, rid3 : integer; + +function readRegister(regId : integer) : real; +begin + if regId <= 1 then + readRegister := r1 + else if regId <= 2 then + readRegister := r2 + else if regId <= 3 then + readRegister := r3 + else if regId <= 4 then + readRegister := r4 + else + readRegister := 0.0 +end; + +procedure writeRegister(regId : integer; number : real); +begin + if regId < 1 then + skip + else if regId <= 1 then + r1 := number + else if regId <= 2 then + r2 := number + else if regId <= 3 then + r3 := number + else if regId <= 4 then + r4 := number + else skip +end; + +begin + r1 := 0.0; + r2 := 0.0; + r3 := 0.0; + r4 := 0.0; + writeln(r1, r2, r3, r4); + readln(option); + while option <> 0 do + begin + if option <= 1 then + begin + readln(rid1, rt); + writeRegister(rid1, rt) + end + else if option <= 2 then + begin + readln(rid1, rid2, rid3); + writeRegister(rid1, readRegister(rid2) + readRegister(rid3)) + end + else if option <= 3 then + begin + readln(rid1, rid2, rid3); + writeRegister(rid1, readRegister(rid2) * readRegister(rid3)) + end + else skip; + writeln(r1, r2, r3, r4); + readln(option) + end +end +. diff --git a/test/input/ipexam-missing.pas b/test/input/ipexam-missing.pas new file mode 100644 index 0000000..2584d97 --- /dev/null +++ b/test/input/ipexam-missing.pas @@ -0,0 +1,21 @@ +{ Example IP exam assignment with a basic while loop } +program missing; +{ Takes a number N, then N unique numbers <= N of which one is 0. Prints the missing number <= N } + +var n, total, a, i : integer; + +begin + readln(n); + total := n; + i := 0; + + while i < n do + begin + readln(a); + total := total + i; + total := total - a; + i := i + 1 + end; + + writeln(total) +end. \ No newline at end of file diff --git a/test/input/ipexam-permutations.pas b/test/input/ipexam-permutations.pas new file mode 100644 index 0000000..e36500a --- /dev/null +++ b/test/input/ipexam-permutations.pas @@ -0,0 +1,63 @@ +program permutations; + +var n, lcm, l, i : integer; +var p, loopLength : array [0 .. 100] of integer; + +function gcd(a : integer; b : integer): integer; +begin + if a mod b <> 0 then + gcd := gcd(b, a mod b) + else + gcd := b +end; + +function loopLengthAt(p : array [0 .. 100] of integer; length : integer; start : integer; current : integer; n : integer; lengths : array [0 .. 100] of integer) : integer; +var isSet, result : integer; +begin + isSet := 0; + if lengths[current] <> 0 then + begin + loopLengthAt := lengths[current]; + isSet := 1 + end + else if current = start then + if n > 0 then + begin + lengths[current] := n; + loopLengthAt := n; + isSet := 1 + end + else skip + else skip; + + if isSet = 0 then + begin + result := loopLengthAt(p, length, start, p[current], n + 1, lengths); + lengths[current] := result; + loopLengthAt := result + end + else skip +end; + +begin + lcm := 1; + i := 0; + readln(n); + + while i < n do + begin + readln(p[i]); + loopLength[i] := 0; + i := i + 1 + end; + + i := 0; + while i < n do + begin + l := loopLengthAt(p, n, i, i, 0, loopLength); + lcm := lcm * l div gcd(lcm, l); + i := i + 1 + end; + + writeln(lcm) +end. \ No newline at end of file diff --git a/test/input/parmechanism.pas b/test/input/parmechanism.pas new file mode 100644 index 0000000..01ef4a4 --- /dev/null +++ b/test/input/parmechanism.pas @@ -0,0 +1,14 @@ +{ Parameter passing mechanism from the lab pdf } +PROGRAM parmechanism; +VAR a, b : integer; + +PROCEDURE proc(VAR a:integer; b, c : integer); +BEGIN b := b + 2 +; a := c*a + b +END; + +BEGIN readln(a) +; b := 0 +; proc(a,b,2) +; writeln(a,b) +END. \ No newline at end of file diff --git a/test/input/prime.pas b/test/input/prime.pas new file mode 100644 index 0000000..d6b6ada --- /dev/null +++ b/test/input/prime.pas @@ -0,0 +1,27 @@ +{ This program determines whether a number is prime } +PROGRAM prime; + +VAR p,c : integer; + +BEGIN + readln(p); + c := 2; + while c * c <= p do + begin + if p mod c <> 0 then + begin + c := c + 1; + if c * c > p then + writeln(1) + else + begin + end + end + else + begin + writeln(0); + c := p + 100 { Exit condition that also works for p = 1 } + end + end +END +. diff --git a/test/input/puzzle.pas b/test/input/puzzle.pas new file mode 100644 index 0000000..06c8f5a --- /dev/null +++ b/test/input/puzzle.pas @@ -0,0 +1,34 @@ +{ This is some puzzle thing from an old IP exam } +program puzzle; + +var len, temp : integer; +var series : array [0 .. 20] of integer; + +function canReachLast(i : integer; pos : integer; len : integer; series : array [0 .. 20] of integer) : integer; +begin + if pos < 0 or pos >= len or i >= len then + canReachLast := 0 + else if len - 1 = pos + series[pos] or canReachLast(i + 1, pos + series[pos], len, series) = 1 then + canReachLast := 1 + else + canReachLast := canReachLast(i + 1, pos - series[pos], len, series) +end; + +function isSolvable(len : integer; series : array [0 .. 20] of integer) : integer; +begin + isSolvable := canReachLast(0, 0, len, series) +end; + +begin + len := 0; + readln(temp); + series[len] := temp; + while series[len] <> 0 do + begin + len := len + 1; + readln(temp); + series[len] := temp + end; + len := len + 1; + writeln(isSolvable(len, series)) +end. \ No newline at end of file diff --git a/test/input/pyth.pas b/test/input/pyth.pas new file mode 100644 index 0000000..fcb8847 --- /dev/null +++ b/test/input/pyth.pas @@ -0,0 +1,41 @@ +{ Pythagoras program } +PROGRAM pyth; +{ + Program that reads three numbers and tells bou + - 0 if they're not a triangle + - 1 if it's a right-angled triangle + - 2 if it's an obtuse triangle + - 3 if it's a sharp triangle +} +VAR a, b, c, t: integer; + +BEGIN + readln(a, b, c); + while a > b do + begin + t := a; + a := b; + b := t + end; {a <= b} + while a > c do + begin + t := a; + a := c; + c := t + end; {a <= c && a <= b} + while b > c do + begin + t := b; + b := c; + c := t + end; {a <= b <= c} + if a + b <= c then + writeln(0) + else if a * a + b * b < c * c then + writeln(2) + else if a * a + b * b > c * c then + writeln(3) + else + writeln(1) +END +. diff --git a/test/input/return42.pas b/test/input/return42.pas new file mode 100644 index 0000000..da46ad7 --- /dev/null +++ b/test/input/return42.pas @@ -0,0 +1,19 @@ +{ Return 42 example from lab pdf } +program return42; + +var x : integer; + +function return42(n : integer) : integer; +begin + if n > 42 then + return42 := return42(n-1) + else begin + return42 := n; + while return42 < 42 do return42 := return42 + 1 + end +end; + +begin + readln(x); + writeln(return42(x)) +end. \ No newline at end of file diff --git a/test/input/shadowing.pas b/test/input/shadowing.pas new file mode 100644 index 0000000..ff8a3b5 --- /dev/null +++ b/test/input/shadowing.pas @@ -0,0 +1,18 @@ +{ This program does a lot of shadowing } +PROGRAM shadowing; + +function x (a : integer) : integer; +begin + x := a +end; + +function a (b : integer) : integer; +var x : integer; +begin + x := b; + a := x +end; + +begin + writeln(a(x(4))) +end. \ No newline at end of file diff --git a/test/input/subarray.pas b/test/input/subarray.pas new file mode 100644 index 0000000..02e9386 --- /dev/null +++ b/test/input/subarray.pas @@ -0,0 +1,23 @@ +{ This program does more stuff with subarrays } +program arr; + +var arr : array [1 .. 10] of real; + +procedure some (rra : array [5 .. 10] of real); +begin + rra[6] := 4 +end; + +procedure some2 (var rra : array [5 .. 10] of real; var x : real); +begin + rra[6] := 4; + x := 3 +end; + +begin + arr[3] := 3; + some(arr[2 .. 7]); + writeln(arr[3]); + some2(arr[2 .. 7], arr[4]); + writeln(arr[3], arr[4]) +end. \ No newline at end of file