From bd240a792bd7fcd2d56678b381abbc84363ab2e2 Mon Sep 17 00:00:00 2001 From: Felix Cherubini Date: Tue, 26 Nov 2024 16:10:02 +0100 Subject: [PATCH 1/2] Explain how to get started --- notes/java.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/notes/java.md b/notes/java.md index 1399df5..6c745c5 100644 --- a/notes/java.md +++ b/notes/java.md @@ -63,10 +63,58 @@ interface TypedExpr { The second record `Var` shows a handy shortcut to overriding the method `type`: if the record has an attribute of the right Type and name ("`type`" in this case), it will automotically become the implementation. -You can work with a variable of type `TypedExpr` like in the example above, by using `switch` expressions. If you want to avoid the `default` case, you can use a `sealed interface` instead. To make that work, you have to declare all implementations using `permits` like this: +You can work with a variable of type `TypedExpr` like in the example above, by using `switch` expressions. If you want to avoid the `default` case, you can use a `sealed interface` instead. To make that work, you have to declare all implementations inside the interface (or use `permits`): ```java -sealed interface TypedExpr permits And, Var { -... +sealed interface TypedExpr { + record And(TypedExpr e1, TypedExpr e2) implements TypedExpr { + Type type() { + return new Type.Bool; + } + + record Var(String name, Type type) implements TypedExpr { } } ``` + +# How to start + +This is just one way to start and the ideas given are not complete - be prepared to change everything along the way to a solution that passes all tests. To get started, here is a possible first try at an implementaion of the `typecheck` function in the `TypeChecker`: + +```java + public AnnotatedProgram typecheck(Program p) { + var funDefs = ((PDefs) p).listdef_.stream() + .map(def -> (DFun) def) + .toList(); + signatures = new HashMap<>(); + extractSignatures(funDefs); + return new AnnotatedProgram(checkFunDefs(funDefs)); + } +``` + +The first statement extracts a list of function definitions (from the parser) from `p` and stores it in `funDefs`. The `var` means that the type of `funDefs` is automatically infered. Then the signatures, which are stored in a field in the `TypeChecker` class, are initialized. To make the last line of this functions work, the type `AnnotatedProgram)` was modified to an inner `record` of the `TypeChecker` in the following way (the functions `extractSignatures` and `checkFunDefs` will be explained below): + +```java + record AnnotatedProgram(Map defs) { } +``` + +The type `TypedFunDef` is also defined as an inner `record` of `TypeChecker`: + +```java + record TypedFunDef(CType returns, List args, List stms) { } + record TypedArg(String name, CType type) { } +``` + +... and `Statement` and `CType` are defined in separate files: + +```java +sealed interface Statement { + record Decl(String varName, CType type) implements Statement {} + // TODO: add more cases +} +``` + +```java +enum CType { + Int, Double, Bool, Void +} +``` \ No newline at end of file From df0090b95b5178b198585ee515dc87ef8b6b15b8 Mon Sep 17 00:00:00 2001 From: Felix Cherubini Date: Tue, 26 Nov 2024 16:31:55 +0100 Subject: [PATCH 2/2] Explain how to get started --- notes/java.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/notes/java.md b/notes/java.md index 6c745c5..525a0c8 100644 --- a/notes/java.md +++ b/notes/java.md @@ -78,7 +78,7 @@ sealed interface TypedExpr { # How to start -This is just one way to start and the ideas given are not complete - be prepared to change everything along the way to a solution that passes all tests. To get started, here is a possible first try at an implementaion of the `typecheck` function in the `TypeChecker`: +This is just one way to start and the ideas given are not complete - be prepared to change everything along the way to a solution that passes all tests. To get started, here is a possible first try at an implementation of the `typecheck` function in the `TypeChecker`: ```java public AnnotatedProgram typecheck(Program p) { @@ -91,7 +91,7 @@ This is just one way to start and the ideas given are not complete - be prepared } ``` -The first statement extracts a list of function definitions (from the parser) from `p` and stores it in `funDefs`. The `var` means that the type of `funDefs` is automatically infered. Then the signatures, which are stored in a field in the `TypeChecker` class, are initialized. To make the last line of this functions work, the type `AnnotatedProgram)` was modified to an inner `record` of the `TypeChecker` in the following way (the functions `extractSignatures` and `checkFunDefs` will be explained below): +The first statement extracts a list of function definitions from (the parse-tree) `p` and stores it in `funDefs`. The `var` means that the type of `funDefs` is automatically infered. Then the signatures, which are stored in a field in the `TypeChecker` class, are initialized. To make the last line of this functions work, the type `AnnotatedProgram)` was modified to an inner `record` of the `TypeChecker` in the following way (the functions `extractSignatures` and `checkFunDefs` will be explained below): ```java record AnnotatedProgram(Map defs) { } @@ -109,7 +109,7 @@ The type `TypedFunDef` is also defined as an inner `record` of `TypeChecker`: ```java sealed interface Statement { record Decl(String varName, CType type) implements Statement {} - // TODO: add more cases + // TODO: add more records for all the statements we need in the typed syntax } ``` @@ -117,4 +117,51 @@ sealed interface Statement { enum CType { Int, Double, Bool, Void } +``` + +A `Type` from the parser syntax can then be translated to a `CType` like this (where this function definition could for example be put into `TypeChecker`): + +```java + CType typeFrom(Type type) { + return switch(type) { + case Type_int ignored -> CType.Int; + // TODO: other cases + default -> throw new IllegalStateException("Unexpected value: " + type); + }; + } +``` + +Now, we'll get back to the functions we left unexplained above, `extractSignatures` and `checkFunDefs`. As the name suggests, `extractSignatures` should pass through all function definitions and extract their signatures, which we will need to check the actual function defintions. This is a possible implementaion: + +```java + void extractSignatures(List funDefs) { + for(var def : funDefs) { + List argTypes = def.listarg_.stream() + .map(arg -> typeFrom(((ADecl) arg).type_)) + .toList(); + Signature signature = new Signature(typeFrom(def.type_), argTypes); + if(signatures.put(def.id_, signature) != null) { + throw new TypeException("Signature already defined for " + def.id_); + }; + } + } +``` + +One thing that could be added here (or somewhere else) is, that there should be exactly one function called "main" which returns `int` and has no arguments. We will only give a sketch of an implementation of `checkFunDefs`. One thing to take care of is dealing with contexts in the right way, which needs some data structure like a stack or list. This is not explained in detail here: + +```java + HashMap checkFunDefs(List funDefs) { + var checkedFunDefs = new HashMap(); + for(var funDef : funDefs) { + // TODO: produce 'args' which should be a list of argument names with their types + // TODO: produce 'contexts' which should be some datastructure storing types of declared variables + TypedFunDef checkedFunDef = new TypedFunDef(typeFrom(funDef.type_), + args, + checkStms(funDef.liststm_, contexts)); + if(checkedFunDefs.put(funDef.id_, checkedFunDef) != null) { + throw new TypeException("Function " + funDef.id_ + " already defined!"); + } + } + return checkedFunDefs; + ``` \ No newline at end of file