Hello, if you have any need, please feel free to consult us, this is my wechat: wx91due
CFL RESIT / REPLACEMENT 2025
The resit / replacement task is CW5 (listed below). It will count for 60% of the final mark. Your task is to lex the files in the examples directory, generate ASTs for the K-intermediate language and supply sufficient type annotations such that you can generate valid code for the LLVM-IR. You should follow the ideas in the file resit.sc and make appropriate additions in order to deal with types. The submission is done via GitHub and the deadline is 31st July 2025 at 16:00. The input fun-files I will use for testing are also uploaded to GitHub. Of help might be the videos and handouts of Week 10. If you have any questions, please email me.
Good Luck!
Coursework 5
• https://bit.ly/3rheZYr
• https://llvm.org/docs/LangRef.html
You can do the implementation of your compiler in any programming language you like, but you need to submit the source code with which you generated the LLVM-IR files, otherwise a mark of 0% will be awarded. You are asked to submit the code of your compiler, but also the generated .ll files. No PDF is needed for this coursework. You should use the lexer and parser from the previous courseworks, but you need to make some modifications to them for the ‘typed’ version of the Fun-language. I will award up to 5% if a lexer and a parser are correctly implemented.
You will be marked according to the input files
• sqr.fun
• fact.fun
• mand.fun
• mand2.fun
• hanoi.fun
Disclaimer
Task
- val Ymin: Double = -1.3;
- val Maxiters: Int = 1000;
def foo(n: Int , x: Double) : Double = ...
def id(n: Int) : Int = ...
def bar () : Void = ...
abstract class Expabstract class BExpabstract class Declcase class Def(name: String , args: List [( String , String )],ty: String , body: Exp) extends Declcase class Main(e: Exp) extends Declcase class Const(name: String , v: Int) extends Declcase class FConst(name: String , x: Double) extends Declcase class Call(name: String , args: List[Exp ]) extends Expcase class If(a: BExp , e1: Exp , e2: Exp) extends Expcase class Var(s: String) extends Expcase class Num(i: Int) extends Exp// integer numberscase class FNum(i: Double) extends Exp // floating numberscase class ChConst(c: Int) extends Exp // char constantscase class Aop(o: String , a1: Exp , a2: Exp) extends Expcase class Sequence(e1: Exp , e2: Exp) extends Expcase class Bop(o: String , a1: Exp , a2: Exp) extends BExp
This datatype distinguishes whether the global constant is an integer constant or floating constant. Also a function definition needs to record the return type of the function, namely the argument ty in Def, and the arguments consist of an pairs of identifier names and types (Int or Double). The hard part of the CW is to design the K-intermediate language and infer all necessary types in order to generate LLVM-IR code. You can check your LLVM-IR code by running it with the interpreter lli.
LLVM-IR
• Global constants: While global constants such as
val Max : Int = 10;
can be easily defined in the LLVM-IR as follows
@Max = global i32 10
%tmp_22 = load i32 , i32* @Max
• Void-Functions: While integer and double functions can easily be called and their results can be allocated to a temporary variable:
%tmp_23 = call i32 @sqr (i32 %n)
call void @print_int (i32 %tmp_23)
def compile_op (op: String) = op match {case "+" => "add i32 "case "*" => "mul i32 "case "-" => "sub i32 "case "==" => "icmp eq i32 "case "!=" => "icmp ne i32 "case "<=" => "icmp sle i32 " // signed less or equalcase "<" => "icmp slt i32 " // signed less than}the corresponding operations on doubles aredef compile_dop (op: String) = op match {case "+" => "fadd double "case "*" => "fmul double "case "-" => "fsub double "case "==" => "fcmp oeq double "case "!=" => "fcmp one double "case "<=" => "fcmp ole double "case "<" => "fcmp olt double "}
define two typing functionsdef typ_val(v: KVal , ts: TyEnv) = ???def typ_exp(a: KExp , ts: TyEnv) = ???
Both functions require a typing-environment that updates the information about what type each variable, operation and so on receives. Once the types are inferred, the LLVM-IR code can be generated. Since we are dealing only with simple first-order functions, nothing on the scale as the ‘Hindley-Milner’ typing-algorithm is needed. I suggest to just look at what data is avaliable and generate all missing information by “simple means”…rather than looking at the literature which solves the problem with much heavier machinery.
• Build-In Functions: The ‘prelude’ comes with several build-in functions: new_line(), skip, print_int(n), print_space(), print_star() and print_char(n).
You can find the ‘prelude’ for example in the file sqr.ll.
// Mandelbrot program (without character constants)val Ymin: Double = -1.3;val Ymax: Double = 1.3;val Ystep: Double = 0.05; //0.025;val Xmin: Double = -2.1;val Xmax: Double = 1.1;val Xstep: Double = 0.02; //0.01;val Maxiters: Int = 1000;def m_iter(m: Int , x: Double , y: Double ,zr: Double , zi: Double) : Void = {if Maxiters <= mthen print_star ()else {if 4.0 <= zi*zi+zr*zr then print_space ()else m_iter(m + 1, x, y, x+zr*zr -zi*zi , 2.0* zr*zi+y)}};def x_iter(x: Double , y: Double) : Void = {if x <= Xmaxthen { m_iter (0, x, y, 0.0, 0.0) ; x_iter(x + Xstep , y) }else skip ()};def y_iter(y: Double) : Void = {if y <= Ymaxthen { x_iter(Xmin , y) ; new_line () ; y_iter(y + Ystep) }else skip ()};y_iter(Ymin)
Figure 2: Ascii output of the Mandelbrot program.