Skip to content

Commit 528e88f

Browse files
Documentation improvements and added main #19
2 parents 623127a + d71ee85 commit 528e88f

File tree

8 files changed

+155
-45
lines changed

8 files changed

+155
-45
lines changed

docs/Notizen zum AST.md

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
11
# Notizen zum AST
22

3-
Die Definition des ASTs wurde stark von den Ansätzen aus der Vorlesung inspiriert. Im folgenden sollen v.a. anders getroffene Entscheidungen begründet werden. Zusätzlich soll kurz erklärt werden, wie das Diagramm genau zu lesen ist. Das ist nötig, weil die Programmiersprache, die zur Erstellung des Compilers genutzt wurde, nicht objekt-orientiert ist und eine Übersetzung von UML-Diagramm zur Definition im Code somit leicht anders als bei Java Programmen ist.
3+
Die Definition des ASTs wurde stark von den Ansätzen aus der Vorlesung inspiriert. Im Folgenden sollen v.a. anders
4+
getroffene Entscheidungen begründet werden. Zusätzlich soll kurz erklärt werden, wie das Diagramm genau zu lesen ist.
5+
Das ist nötig, weil die Programmiersprache, die zur Erstellung des Compilers genutzt wurde, nicht objekt-orientiert ist
6+
und eine Übersetzung von UML-Diagramm zur Definition im Code somit leicht anders als bei Java Programmen ist.
47

58
## Erklärung des Diagramms
69

7-
Einige Klassen des Diagramms haben einen blauen Rand. Das zeigt an, dass diese Elemente noch nicht beim Parsen erstellt werden, sondern erst beim Typcheck hinzugefügt werden. Speziell werden alle Statements, Expression und Statemnt-Expressions in TypedStmt, TypedExpr bzw. TypedStmtExpr gepackt. Somit kann Typ-Information hinzugefügt werden, die beim Parsing noch nicht vorhanden war. Auch werden alle LocalOrFieldVar Objekte bei der Typisierung in LocalVar bzw. FieldVar Objekte transformiert. Der Grund dahinter liegt darin, dass der Typchecker sowieso herausfinden muss, ob es sich bei der Variable um eine lokale Variable oder ein Attribut der Klasse handelt. Da die Codegenerierung diese Informationen ebenfalls benötigt, werden die dann beim Typchecker direkt in den AST geliefert.
10+
Einige Klassen des Diagramms haben einen blauen Rand.
11+
Das zeigt an, dass diese Elemente noch nicht beim Parsen erstellt werden, sondern erst beim Typcheck hinzugefügt werden.
12+
Speziell werden alle Statements, Expression und Statement-Expressions in TypedStmt, TypedExpr bzw. TypedStmtExpr gepackt.
13+
Somit kann Typ-Information hinzugefügt werden, die beim Parsing noch nicht vorhanden war. Auch werden alle `LocalOrFieldVar`
14+
Objekte bei der Typisierung in LocalVar bzw. FieldVar Objekte transformiert. Der Grund dahinter liegt darin, dass der
15+
Typchecker sowieso herausfinden muss, ob es sich bei der Variable um eine lokale Variable oder ein Attribut der Klasse
16+
handelt. Da die Codegenerierung diese Informationen ebenfalls benötigt, werden die dann beim Typchecker direkt in den
17+
AST geliefert.
818

9-
Im Quellcode werden viele der "Klassen" des Diagramms als Enumerations dargestellt. Das liegt daran, dass die gewählte Programmiersprache - [Rust](https://www.rust-lang.org/) - sehr mächtige Enumerations zur Verfügung stellt. Bei Rust kann jeder Option aus einem Enum eine eigene Menge an Parametern gegeben werden. Somit können alle Attribute der Klasse als Parameter des Enums abgebildet werden. Speziell wurde das bei der Definition von `Stmt`, `Expr` und `StmtExpr` genutzt. Da alle diese Elemente nicht wirklich von eine Überklasse erben, sondern eigentlich Optionen eines Enums sind, wurden sie im Diagramm als einzelne Klassen in einer Box, die das Enum darstellt, abgebildet.
19+
Im Quellcode werden viele der "Klassen" des Diagramms als Enumerations dargestellt. Das liegt daran, dass die gewählte
20+
Programmiersprache - [Rust](https://www.rust-lang.org/) - sehr mächtige Enumerations zur Verfügung stellt.
21+
Bei Rust kann jeder Variante aus einem Enum eine eigene Menge an Parametern gegeben werden. Somit können alle Attribute
22+
der Klasse als Parameter des Enums abgebildet werden. Speziell wurde das bei der Definition von `Stmt`, `Expr` und
23+
`StmtExpr` genutzt. Da alle diese Elemente nicht wirklich von eine Überklasse erben, sondern eigentlich Varianten eines
24+
Enums sind, wurden sie im Diagramm als einzelne Klassen in einer Box, die das Enum darstellt, abgebildet.
1025

1126
## Erklärung unserer Entscheidungen
1227

13-
TBD
14-
15-
- FieldDecl hat optional value Attribut
16-
- ...
28+
- Assign nimmt eine expression für die Variable auf, da wir bei der Codegenerierung wissen müssen um welchen Typ an Variable es sich handelt, da dort dann unterschiedlicher Bytecode generiert werden muss
29+
- FieldDecl nimmt einen optionalen Wert zur direkten Zuweisung auf
30+
- Type wurde um Class erweitert um die Codegenerierung zu vereinfachen

docs/Project-Doc.md

+7-5
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
- Wer hat welche Arbeit am Parser gemacht?
77
- Wie wurde Pest fürs Parsing eingesetzt? (sehr ähnlich zu ANTLR)
88

9-
## Typchecker
9+
## Typechecker
1010

11-
Geschrieben von: Maximilian Floto, Philipp Wolf
11+
Geschrieben von: Maximilian Floto und Philipp Wolf im Pair Programming
1212

1313
Der Typechecker akzeptiert einen Abstract Syntax Tree (AST) und gibt einen getypten AST (TAST) zurück.
1414
Er führt eine umfassende Analyse durch, um die Typen aller Variablen und Ausdrücke im Code zu bestimmen.
@@ -44,9 +44,11 @@ Folgende Fehler werden vom Typechecker erkannt:
4444

4545
## Codegenerierung
4646

47-
Geschrieben von: Marion Hinkel, Benedikt Brandmaier, Val Richter
48-
- Wer hat welche Arbeit bei codegen gemacht?
49-
- Was für Arbeit musste alles zusätzlich getan werden, weil wir nicht Java + ASM genutzt haben?
47+
ByteCode-Umwandlung, Bugfixes, StackSize und Improvements: Val Richter
48+
49+
Definition DIR(Intermediate Representation), ConstantPool, LocalVarPool, Methoden zur Instruction-generierung, BugFixes, etwas ByteCode-Umwandlung und Umwandlung relativer in absolute Jumps: Marion Hinkel und Benedikt Brandmaier im Pair Programming
50+
- Wer hat welche Arbeit bei codegen gemacht?
51+
- Was für Arbeit musste alles zusätzlich getan werden, weil wir nicht Java + ASM genutzt haben?
5052

5153
## Testing
5254

docs/User-Doc.md

+23-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,36 @@
11
TBD
22

3-
- Jeweils erwähnen wie man die Teile kompiliert und laufen lassen kann. Nicht vergessen: Wie sollte die Eingabe aussehen und wie sieht die Ausgabe dann aus?
4-
- Vielleicht sollten wir Command-Line Befehle für jeden der Teile im Binary Crate zur Verfügung stellen?
3+
- Jeweils erwähnen wie man die Teile kompiliert und laufen lassen kann. Nicht vergessen: Wie sollte die Eingabe aussehen und wie sieht die Ausgabe dann aus?
4+
- Vielleicht sollten wir Command-Line Befehle für jeden der Teile im Binary Crate zur Verfügung stellen?
5+
6+
## Vorbedingungen
7+
[Rust installieren](https://rustup.rs/):
8+
```bash
9+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
10+
```
11+
- [JDK 20](https://www.oracle.com/java/technologies/downloads/)(Wird in Tests für die Validierung des Codegens benötigt)
12+
13+
# Ausführen
14+
Um eine .java in eine .class-datei zu kompilieren:
15+
```bash
16+
cargo run -- <input_file>
17+
```
18+
19+
# Testen
20+
1. Projekt bauen: `cargo build`
521

622
## Parser
723

24+
2. Parser tests ausführen: `cargo test test_parser`
825
## Typchecker
926

10-
1. Projekt bauen: `cargo build`
11-
2. Go to lib folder: `cd lib`
12-
3. Typechecker tests ausführen: `cargo test test_typechecker`
27+
2. Typechecker tests ausführen: `cargo test test_typechecker`
1328

1429
Spezifischen Test ausführen: `cargo test <test_name>::test_typechecker`
1530

1631
## Codegenerierung
1732

33+
2. Codegenerierung tests ausführen: `cargo test test_codegen`
34+
1835
## Testing
36+
Zur ausführung aller tests: `cargo test`

lib/src/codegen/ir.rs

+51-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![allow(non_snake_case)]
44

55
use crate::codegen::reljumps::convert_to_absolute_jumps;
6+
use crate::codegen::Instruction::getfield;
67
use crate::types::Expr::Binary;
78
use crate::types::*;
89
use std::fmt::Debug;
@@ -844,7 +845,27 @@ fn generate_code_stmt_expr(
844845
result.push(Instruction::putfield(idx));
845846
stack.update(-2);
846847
}
847-
_ => panic!("Unexpected variable type for assignment"),
848+
Expr::InstVar(expr, name) => {
849+
// FIXME: Searches for something here
850+
let idx = constant_pool.add(Constant::FieldRef(FieldRef {
851+
class: class_name.to_string(),
852+
field: NameAndType {
853+
name: name.to_string(),
854+
r#type: t.to_ir_string(),
855+
},
856+
}));
857+
result.append(&mut generate_code_expr(
858+
expr.deref().clone(),
859+
stack,
860+
constant_pool,
861+
local_var_pool,
862+
class_name,
863+
));
864+
result.append(&mut expr_code);
865+
result.push(Instruction::putfield(idx));
866+
stack.update(-2);
867+
}
868+
_ => panic!("Unexpected variable type for assignment: {:?}", var),
848869
},
849870
_ => panic!("Expected typed stmt"),
850871
}
@@ -867,7 +888,6 @@ fn generate_code_stmt_expr(
867888
stack.update(-1);
868889
}
869890
StmtExpr::MethodCall(_expr, name, args) => {
870-
// FIXME: Needs to update stack + probably doesn't work yet anyways
871891
// Generate bytecode for method call
872892
// Principally this should work this way:
873893
// 1. Write Function Name into Constant Pool generating the necessary Constants
@@ -967,10 +987,35 @@ fn generate_code_expr(
967987
stack.update(1);
968988
}
969989
Expr::InstVar(exprs, name) => {
970-
result.push(Instruction::aload(0));
971-
stack.update(1);
972-
973-
// FIXME: There should be more here, right?
990+
match exprs.deref() {
991+
Expr::TypedExpr(expr, r#type) => match expr.deref() {
992+
Expr::This => {
993+
result.push(Instruction::aload(0));
994+
result.push(Instruction::getfield(constant_pool.add(
995+
Constant::FieldRef(FieldRef {
996+
class: class_name.to_string(),
997+
field: NameAndType {
998+
name: name.clone(),
999+
r#type: r#type.to_ir_string(),
1000+
},
1001+
}),
1002+
)));
1003+
}
1004+
_ => panic!("Expected this got {:?}", exprs),
1005+
},
1006+
_ => panic!("Expected typed stmt got {:?}", exprs),
1007+
}
1008+
let field_index = constant_pool.add(Constant::FieldRef(FieldRef {
1009+
class: class_name.to_string(),
1010+
field: NameAndType {
1011+
name: name.clone(),
1012+
r#type: r#type.to_ir_string(),
1013+
},
1014+
}));
1015+
result.push(getfield(field_index));
1016+
// I'm thinking 2 here since we load the field here too and leave the class on the stack
1017+
stack.update(2);
1018+
// TODO: Val check if thats correct pls
9741019
}
9751020

9761021
Binary(op, left, right) => {

lib/src/parser/parser.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extern crate pest_derive;
88
use crate::types::{Class, Expr, FieldDecl, MethodDecl, Stmt, StmtExpr, Type};
99
use pest::error::Error;
1010
use pest::iterators::{Pair, Pairs};
11+
use pest::unicode::RUNIC;
1112
use pest::Parser;
1213
use pest_derive::Parser;
1314

@@ -26,7 +27,8 @@ pub fn parse_programm(file: &str) -> Result<Vec<Class>, Error<Rule>> {
2627
}
2728

2829
fn parse_class(pair: Pair<Rule>) -> Class {
29-
match pair.as_rule() {
30+
let rule = pair.as_rule();
31+
match rule {
3032
Rule::ClassDecl => {
3133
let mut inners = pair.into_inner();
3234
let other_name = next_id(&mut inners);
@@ -51,7 +53,7 @@ fn parse_class(pair: Pair<Rule>) -> Class {
5153
methods,
5254
}
5355
}
54-
_ => todo!(),
56+
_ => unreachable!(),
5557
}
5658
}
5759
fn next_id(inners: &mut Pairs<Rule>) -> String {
@@ -360,17 +362,26 @@ fn parse_expr(pair: Pair<Rule>) -> Expr {
360362
obj
361363
}
362364
Rule::UnaryExpr => {
363-
todo!()
364-
}
365-
Rule::ParanthesizedExpr => {
366-
todo!()
365+
let mut inners = pair.into_inner();
366+
let unaryOp = inners.next().unwrap().as_str().trim().to_string();
367+
let second = inners.next().unwrap();
368+
let result = if second.as_rule() == Rule::Identifier {
369+
Expr::LocalOrFieldVar(second.as_str().trim().to_string()) //@Notice: duno if this is corect
370+
} else {
371+
parse_expr(second)
372+
};
373+
Expr::Unary(unaryOp, Box::new(result))
367374
}
375+
Rule::ParanthesizedExpr => parse_expr(pair.into_inner().next().unwrap()),
368376
Rule::IntLiteral => Expr::Integer(pair.as_str().parse().unwrap()),
369377
Rule::BoolLiteral => Expr::Bool(pair.as_str().parse().unwrap()),
370378
Rule::CharLiteral => Expr::Char(get_str_content(pair.as_str()).parse().unwrap()),
371379
Rule::StrLiteral => Expr::String(get_str_content(pair.as_str()).to_string()),
372380
Rule::StmtExpr => Expr::StmtExprExpr(Box::new(parse_StmtExpr(pair))),
373381
Rule::NonBinaryExpr => parse_expr(pair.into_inner().next().unwrap()),
382+
Rule::Prec3BinExpr => {
383+
todo!()
384+
}
374385
_ => {
375386
dbg!(pair.as_rule());
376387
unreachable!()

lib/src/tests/mod.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use tast_to_ast::*;
4444
fn normalize_str(s: std::string::String) -> std::string::String {
4545
s.split('\n')
4646
.fold("".to_string(), |acc, s| acc + s)
47-
.split("\t")
47+
.split('\t')
4848
.fold("".to_string(), |acc, s| acc + s)
4949
}
5050

@@ -122,7 +122,7 @@ pub fn codegen_test(tast: &Class, name: &str) {
122122

123123
File::create(format! {"lib/testcases/{name}-Test.java"})
124124
.expect("failed to create Test.java")
125-
.write(java_code.as_bytes())
125+
.write_all(java_code.as_bytes())
126126
.expect("failed to write to Test.java");
127127
// Compile original java code for expected result
128128
compile_java(name);
@@ -137,10 +137,10 @@ pub fn codegen_test(tast: &Class, name: &str) {
137137
// Compile & run tests on generated DIR
138138
let mut dir = generate_dir(&vec![tast.clone()]);
139139
let generated_bytes = dir.as_bytes();
140-
File::create(format!("lib/testcases/{name}.class"))
141-
.expect(&format!("failed to create {name}.class"))
142-
.write(&generated_bytes)
143-
.expect(&format!("failed to write generated DIR into {name}.class"));
140+
File::create(format!("lib/testcases/{name}-generated.class"))
141+
.unwrap_or_else(|_| panic!("failed to create {name}-generated.class"))
142+
.write_all(&generated_bytes)
143+
.unwrap_or_else(|_| panic!("failed to write generated DIR into {name}.class"));
144144
println!("Generated bytes: {:?}", generated_bytes);
145145
disassemble_java(name, &format!("{name}-codegen")); // Probably useful for debugging
146146
compile_java(&format!("{name}-Test"));
@@ -174,11 +174,11 @@ pub fn class_test(ast: &Class, tast: Option<&Class>, name: &str) {
174174
println!("{class_code}");
175175
let mut file = File::create(format!("lib/testcases/{name}.java"))
176176
.expect("failed to open original java file for writing generated code");
177-
file.write(class_code.as_bytes())
177+
file.write_all(class_code.as_bytes())
178178
.expect("failed to write generated java code in original java file");
179179
let mut file = File::create(format!("lib/testcases/{name}-expected.java")) // Only for debugging tests
180180
.expect("failed to open generated java file for writing generated code");
181-
file.write(class_code.as_bytes())
181+
file.write_all(class_code.as_bytes())
182182
.expect("failed to write generated java code in generated java file");
183183

184184
// Compile generated java code
@@ -188,7 +188,7 @@ pub fn class_test(ast: &Class, tast: Option<&Class>, name: &str) {
188188
// Compile original java code
189189
let mut file = File::create(format!("lib/testcases/{name}.java"))
190190
.expect("failed to open original java file for writing");
191-
file.write(og_java_code.as_bytes())
191+
file.write_all(og_java_code.as_bytes())
192192
.expect("failed to write original java code back");
193193
compile_java(name);
194194
let og_clz = disassemble_java(name, &format!("{name}"));
@@ -228,7 +228,7 @@ fn disassemble_java(name: &str, out: &str) -> Vec<u8> {
228228
.arg(clz_file_path)
229229
.stdout(Stdio::from(file))
230230
.spawn()
231-
.expect(&format!("failed to disassemble {name}.java"));
231+
.unwrap_or_else(|_| panic!("failed to disassemble {name}.java"));
232232
let ecode = child
233233
.wait()
234234
.unwrap_or_else(|_| panic!("failed to wait on child disassembling {name}.java"));

lib/src/types.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ impl Type {
254254
Type::Bool => "Z",
255255
Type::String => "Ljava/lang/String;",
256256
Type::Void => "V",
257-
// TODO: Either the class has the formatting `L<class>;' or we have to add it here.
257+
// FIXME: Either the class has the formatting `L<class>;' or we have to add it here.
258258
Type::Class(name) => name,
259259
_ => panic!("Invalid type: {}", self),
260260
}

src/main.rs

+25-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,36 @@
11
use lib::codegen::generate_dir;
2+
use lib::parser::parse_programm;
23
use lib::typechecker::typechecker;
4+
use lib::typechecker::typechecker::TypeChecker;
35
use lib::types::{Class, Expr, FieldDecl, MethodDecl, Prg, Stmt, StmtExpr, Type};
46
use serde_json::Value;
5-
use std::fs::File;
6-
use std::io::Read;
7+
use std::fs::{read_to_string, File};
8+
use std::io::{Read, Write};
79
use tracing::info;
810

911
fn main() -> color_eyre::Result<()> {
1012
color_eyre::install()?;
1113
tracing_subscriber::fmt::init();
12-
info!("Hello RustyJ!");
13-
14-
lib::hi();
14+
let args = std::env::args().collect::<Vec<_>>();
15+
let input_file = args.get(1).unwrap_or_else(|| {
16+
panic!("No input file provided. Usage: {} <input_file>", args[0]);
17+
});
18+
info!("Parsing the file {}", args[1]);
19+
let mut file = read_to_string(&args[1])?;
20+
let prg_parsed = parse_programm(&file)?;
21+
info!("Typechecking the program...");
22+
let prg_typechecked = TypeChecker::new(prg_parsed)
23+
.unwrap_or_else(|e| panic!("{}", e))
24+
.check_and_type_program()
25+
.unwrap_or_else(|e| panic!("{}", e));
26+
info!("Generating code using ducc...");
27+
// Generate code using codegen_ducc
28+
let mut dir = generate_dir(&prg_typechecked);
29+
let bytes = dir.as_bytes();
30+
let alt_outfile = "out.class".to_string();
31+
let out_file = args.get(2).unwrap_or(&alt_outfile);
32+
info!("Writing code to {}", out_file);
33+
let mut file = File::create(out_file)?;
34+
file.write_all(bytes.as_slice())?;
1535
Ok(())
1636
}

0 commit comments

Comments
 (0)