Browse Source

It compiles!

Full compilation of an executable!
dhasenan 2 years ago
parent
commit
f43a6834d2

+ 20
- 8
jpetalc/src/main/java/org/ikeran/petal/Compiler.java View File

@@ -22,6 +22,8 @@ import org.apache.commons.cli.CommandLine;
22 22
 import org.apache.commons.cli.DefaultParser;
23 23
 import org.apache.commons.cli.Options;
24 24
 import org.apache.commons.cli.ParseException;
25
+import org.ikeran.petal.llvm.LlvmPipeline;
26
+import org.ikeran.petal.llvm.TargetType;
25 27
 import org.ikeran.petal.semantic.AntlrToSemanticVisitor;
26 28
 import org.ikeran.petal.semantic.SemNode;
27 29
 import org.ikeran.petal.semantic.SemPhase1;
@@ -67,6 +69,9 @@ public class Compiler {
67 69
             }
68 70
         }
69 71
 
72
+        if (cli.hasOption("irdir")) {
73
+            c.irDir = cli.getOptionValue("irdir");
74
+        }
70 75
         if (cli.hasOption("dump-tokens")) {
71 76
             c.dumpTokensToStdout = true;
72 77
         }
@@ -88,7 +93,7 @@ public class Compiler {
88 93
 
89 94
     public static final int LEX = 1, PARSE = 2, SEM = 3, IR = 4, COMPILE = 5, LINK = 6;
90 95
     public int targetStage = LINK;
91
-    public String irDir;
96
+    public String irDir = "ir";
92 97
     public String outputFilename;
93 98
     public boolean dumpTokensToStdout = false;
94 99
     public boolean dumpParseTreeToStdout = false;
@@ -167,13 +172,20 @@ public class Compiler {
167 172
             return errors.getErrors();
168 173
         }
169 174
         if (targetStage < IR) return Arrays.asList();
170
-        IrVisitor ir = new IrVisitor(irDir);
171
-        ir.visit(tree);
172
-        if (targetStage < COMPILE) return Arrays.asList();
173
-        LLVMContext context = new LLVMContext();
174
-        context.compile(ir.files);
175
-        if (targetStage < LINK) return Arrays.asList();
176
-        context.link();
175
+        LlvmPipeline pipeline = new LlvmPipeline(Arrays.asList(fileNode));
176
+        pipeline.setIrDir(Paths.get(irDir));
177
+        switch (targetStage) {
178
+        case IR:
179
+            pipeline.setTargetType(TargetType.IR);
180
+            break;
181
+        case COMPILE:
182
+            pipeline.setTargetType(TargetType.OBJ);
183
+            break;
184
+        case LINK:
185
+            pipeline.setTargetType(TargetType.EXE);
186
+            break;
187
+        }
188
+        pipeline.run();
177 189
         return Arrays.asList();
178 190
     }
179 191
 

+ 2
- 1
jpetalc/src/main/java/org/ikeran/petal/Errors.kt View File

@@ -47,7 +47,8 @@ enum class ErrorType(val id: Int, val message: String) {
47 47
     TYPE_NOT_FOUND(7, "Type not found"),
48 48
     OPERATOR_UNSUPPORTED(8, "This type does not support this operator"),
49 49
     UNDEFINED_VARIABLE(9, "Variable is not defined"),
50
-    FORWARD_REFERENCE(10, "Declaration cannot be used before it is declared")
50
+    FORWARD_REFERENCE(10, "Declaration cannot be used before it is declared"),
51
+    MISSING_RETURN(11, "Functions with return type other than unit must return a value")
51 52
 }
52 53
 
53 54
 data class Loc(val file: String, val line: Int, val col: Int) {

+ 0
- 11
jpetalc/src/main/java/org/ikeran/petal/IrVisitor.java View File

@@ -1,11 +0,0 @@
1
-package org.ikeran.petal;
2
-
3
-import java.util.List;
4
-
5
-public class IrVisitor extends PetalBaseVisitor<Void> {
6
-	public List<String> files;
7
-	
8
-	public IrVisitor(String irDir) {
9
-	}
10
-
11
-}

+ 0
- 17
jpetalc/src/main/java/org/ikeran/petal/LLVMContext.java View File

@@ -1,17 +0,0 @@
1
-package org.ikeran.petal;
2
-
3
-import java.util.List;
4
-import org.slf4j.Logger;
5
-import org.slf4j.LoggerFactory;
6
-
7
-public class LLVMContext {
8
-	private static Logger logger = LoggerFactory.getLogger(LLVMContext.class);
9
-	
10
-	public void compile(List<String> files) {
11
-		logger.info("compiling %s llvm ir files", files.size());
12
-	}
13
-	
14
-	public void link() {
15
-		logger.info("linking");
16
-	}
17
-}

+ 197
- 0
jpetalc/src/main/java/org/ikeran/petal/llvm/LlvmBitcodeVisitor.kt View File

@@ -0,0 +1,197 @@
1
+package org.ikeran.petal.llvm
2
+
3
+import org.ikeran.petal.semantic.*
4
+import org.ikeran.petal.*
5
+import java.io.PrintWriter
6
+import java.nio.file.Files
7
+import java.nio.file.Paths
8
+import java.nio.file.Path
9
+import java.util.LinkedList
10
+import java.util.Scanner
11
+
12
+enum class TargetType {
13
+    EXE,
14
+    OBJ,
15
+    IR,
16
+    SHARED,
17
+    STATIC,
18
+}
19
+
20
+class BackendException(msg: String): RuntimeException(msg)
21
+
22
+class LlvmPipeline(val topLevelNodes: Collection<SemNode>) {
23
+    var irDir = Paths.get(".tmp-ir")
24
+    var allAtOnce = true
25
+    var optimize = false
26
+    var targetType = TargetType.EXE
27
+
28
+    fun run() {
29
+        var name = irDir.resolve("out.ll")
30
+        val obj = irDir.resolve("out.o")
31
+        val exe = Paths.get("a.out")
32
+        emit()
33
+        if (optimize) {
34
+            val optName = irDir.resolve("out.bc")
35
+            runProcess("opt", name.toString(), "-o", optName.toString())
36
+            name = optName
37
+        }
38
+        if (targetType != TargetType.IR) {
39
+            compile(name, obj)
40
+            if (targetType != TargetType.OBJ) {
41
+                link(obj, exe)
42
+            }
43
+        }
44
+    }
45
+
46
+    fun compile(p: Path, output: Path) {
47
+        runProcess("llc", p.toString(), "-o", output.toString(), "-filetype=obj")
48
+    }
49
+
50
+    fun link(p: Path, output: Path) {
51
+        runProcess("clang", p.toString(), "-o", output.toString())
52
+    }
53
+
54
+    fun runProcess(vararg args: String) {
55
+        val proc = ProcessBuilder(*args)
56
+            .redirectErrorStream(true)
57
+            .start()
58
+        val scan = Scanner(proc.inputStream).useDelimiter("\\A")
59
+        val output = if (scan.hasNext()) scan.next() else ""
60
+        proc.waitFor()
61
+        if (proc.exitValue() != 0) {
62
+            throw BackendException(output)
63
+        }
64
+    }
65
+
66
+    fun emit() {
67
+        val stream = Files.newOutputStream(irDir.resolve("out.ll"))
68
+        val pw = PrintWriter(stream)
69
+        val declqueue = BitcodeDeclQueueVisitor()
70
+        for (node in topLevelNodes) {
71
+            declqueue.visit(node)
72
+        }
73
+        Files.createDirectories(irDir)
74
+        val declvisitor = DeclVisitor(pw)
75
+        for (decl in declqueue.declqueue) {
76
+            logger.info("visiting decl ${decl}")
77
+            declvisitor.visit(decl)
78
+        }
79
+        val initBuilder = InitFuncVisitor(pw)
80
+        initBuilder.buildInit()
81
+        pw.close()
82
+        stream.close()
83
+    }
84
+}
85
+
86
+class BitcodeDeclQueueVisitor: SemVisitor<Unit>() {
87
+    override fun defaultValue() {
88
+    }
89
+
90
+    val declqueue = HashSet<Decl>()
91
+    val initqueue = HashSet<VarDecl>()
92
+    var inFunc: Int = 0
93
+
94
+    override fun visitFuncDecl(node: FuncDecl) {
95
+        inFunc++
96
+        super.visitFuncDecl(node)
97
+        inFunc--
98
+    }
99
+
100
+    override fun visit(node: SemNode) {
101
+        if (node is Decl) {
102
+            observeDecl(node)
103
+        }
104
+        super.visit(node)
105
+    }
106
+
107
+    fun observeDecl(node: Decl) {
108
+        if (node is VarDecl) {
109
+            if (inFunc > 0) {
110
+                return
111
+            }
112
+            if (node.init != null || node.defaultInit) {
113
+                logger.info("adding decl ${node} to init queue")
114
+                initqueue.add(node)
115
+            }
116
+        }
117
+        logger.info("adding decl ${node} to decl queue")
118
+        declqueue.add(node)
119
+    }
120
+}
121
+
122
+class DeclVisitor(val w: PrintWriter): SemVisitor<Unit>() {
123
+    override fun defaultValue() {}
124
+    val funcs = FuncVisitor(w)
125
+
126
+    override fun visitFuncDecl(node: FuncDecl) {
127
+        funcs.emit(node)
128
+    }
129
+}
130
+
131
+class ConstantDataVisitor(val w: PrintWriter): SemVisitor<Unit>() {
132
+    override fun defaultValue() {}
133
+    var constantCount = 0
134
+
135
+    override fun visitStringLiteral(node: StringLiteral) {
136
+        constantCount++
137
+        node.llvmRef = "@str${constantCount}"
138
+        //val bytes = node.value.getBytes(StandardCharsets.UTF_8)
139
+        //w.print(node.llvmRef)
140
+        //w.print()
141
+    }
142
+}
143
+
144
+class FuncVisitor(val w: PrintWriter): SemVisitor<Unit>() {
145
+    var baseScope: Scope? = null
146
+    var localCount = 0
147
+    fun emit(node: FuncDecl) {
148
+        if (node.emitted) return
149
+        node.emitted = true
150
+        baseScope = node.scope
151
+        localCount = 0
152
+        val funcType = node.type as FuncType
153
+        w.printf("define %s @%s(", funcType.returnType.llvmRef, node.mangledName)
154
+        var first = true
155
+        for (arg in node.args) {
156
+            if (!first) w.printf(", ")
157
+            first = false
158
+            w.printf("%s %%%s", arg.type.llvmRef, arg.name)
159
+        }
160
+        w.printf(") {\n")
161
+        visit(node.body)
162
+        w.printf("}\n")
163
+    }
164
+
165
+    override fun visitReturnStatement(node: ReturnStatement) {
166
+        visit(node.expr)
167
+        if (node.expr is UnitLiteral) {
168
+            w.print("  ret void\n")
169
+        }
170
+        w.printf("  ret %s %s\n", node.expr.type.llvmRef, node.expr.llvmRef)
171
+    }
172
+
173
+    override fun visitFloatLiteral(node: FloatLiteral) {
174
+        node.llvmRef = node.value.toString()
175
+    }
176
+
177
+    override fun visitIntLiteral(node: IntLiteral) {
178
+        node.llvmRef = node.value.toString()
179
+    }
180
+
181
+    // don't descend into these decls
182
+    override fun visitFuncDecl(node: FuncDecl) {}
183
+    override fun visitTypeDecl(node: TypeDecl) {}
184
+
185
+    override fun visitOperatorExpr(node: OperatorExpr) {
186
+    }
187
+
188
+    override fun defaultValue() {}
189
+    fun makeLocal(): String = "%${localCount++}"
190
+}
191
+
192
+class InitFuncVisitor(val w: PrintWriter): SemVisitor<Unit>() {
193
+    override fun defaultValue() {}
194
+
195
+    fun buildInit() {}
196
+}
197
+

+ 51
- 0
jpetalc/src/main/java/org/ikeran/petal/semantic/SemPhase1.kt View File

@@ -2,6 +2,7 @@ package org.ikeran.petal.semantic
2 2
 
3 3
 import org.ikeran.petal.ErrorRecorder
4 4
 import org.ikeran.petal.ErrorType
5
+import org.ikeran.petal.Ice
5 6
 import org.slf4j.LoggerFactory
6 7
 
7 8
 class SemPhase1(val errors: ErrorRecorder): SemVisitor<Unit>() {
@@ -14,6 +15,56 @@ class SemPhase1(val errors: ErrorRecorder): SemVisitor<Unit>() {
14 15
         }
15 16
         super.visit(node)
16 17
     }
18
+
19
+    override fun visitFuncDecl(node: FuncDecl) {
20
+        // The main work we do here is building the function's type.
21
+        val rettype = node.rettype.resolve()
22
+        val argTypes = node.args.map { it.type }
23
+        val type = FuncType(rettype, argTypes)
24
+        node.type = type
25
+        super.visitFuncDecl(node)
26
+    }
27
+
28
+    override fun visitReturnStatement(node: ReturnStatement) {
29
+        var scope = node.scope
30
+        Ice.enforce(scope != null, "return statement has no associated scope")
31
+        while (scope != null) {
32
+            if (scope.owner is FuncDecl) {
33
+                break
34
+            }
35
+            scope = scope.parent
36
+        }
37
+        Ice.enforce(scope != null, "failed to find scope")
38
+        if (scope == null) return
39
+        val func = scope.owner as FuncDecl
40
+        val type = func.type
41
+        if (type is ErrorType) return
42
+        if (type is UnknownType) return
43
+        if (type !is FuncType) {
44
+            Ice.enforce(false, "function type should be a FuncType, got ${type}")
45
+            return
46
+        }
47
+        val returnType = type.returnType
48
+        visit(node.expr)
49
+        if (!node.expr.type.canImplicitConvertTo(returnType)) {
50
+            errors.report(
51
+                node,
52
+                ErrorType.TYPE_MISMATCH,
53
+                "cannot convert `${node.expr}` of type ${node.expr.type.name} " +
54
+                "to ${returnType.name}")
55
+        }
56
+        if (node.expr.type != returnType) {
57
+            if (node.expr.type == Type.NUMLITERAL || node.expr.type == Type.INTLITERAL) {
58
+                node.expr.type = returnType
59
+            } else {
60
+                val childAst = node.expr.ast
61
+                val cast = CastExpr(node.expr, returnType, implicit = true)
62
+                cast.ast = childAst
63
+                node.expr = cast
64
+            }
65
+        }
66
+    }
67
+
17 68
     override fun visitVarDecl(node: VarDecl) {
18 69
         logger.info("visiting a VarDecl!")
19 70
         val init = node.init

+ 2
- 4
jpetalc/src/main/java/org/ikeran/petal/semantic/SemVisitor.kt View File

@@ -32,10 +32,7 @@ abstract class SemVisitor<T> {
32 32
     }
33 33
 
34 34
     open fun visitReturnStatement(node: ReturnStatement): T {
35
-        if (node.expr != null) {
36
-            return visit(node.expr)
37
-        }
38
-        return defaultValue()
35
+        return visit(node.expr)
39 36
     }
40 37
 
41 38
     open fun visitBlockStatement(node: BlockStatement): T {
@@ -118,6 +115,7 @@ abstract class SemVisitor<T> {
118 115
     open fun visitIntLiteral(node: IntLiteral): T { return defaultValue() }
119 116
     open fun visitFloatLiteral(node: FloatLiteral): T { return defaultValue() }
120 117
     open fun visitBoolLiteral(node: BoolLiteral): T { return defaultValue() }
118
+    open fun visitUnitLiteral(node: UnitLiteral): T { return defaultValue() }
121 119
     open fun visitInvokeExpr(node: InvokeExpr): T { return defaultValue() }
122 120
     open fun visitCastExpr(node: CastExpr): T {
123 121
         return visit(node.child)

+ 30
- 15
jpetalc/src/main/java/org/ikeran/petal/semantic/SemanticNodes.kt View File

@@ -15,8 +15,8 @@ class Scope(val parent: Scope?, val owner: SemNode) {
15 15
 }
16 16
 
17 17
 abstract class SemNode {
18
-	var ast: ParserRuleContext? = null
19
-	var parent: SemNode? = null
18
+    var ast: ParserRuleContext? = null
19
+    var parent: SemNode? = null
20 20
     var scope: Scope? = null
21 21
     var backendName: String = ""
22 22
 
@@ -30,21 +30,21 @@ class FuncArgs: SemNode() {
30 30
 }
31 31
 abstract class Statement: SemNode() {
32 32
 }
33
-class ReturnStatement(val expr: Expr? = null): Statement() {
33
+class ReturnStatement(var expr: Expr): Statement() {
34 34
     override fun <T> accept(visitor: SemVisitor<T>): T {
35 35
         return visitor.visitReturnStatement(this)
36 36
     }
37 37
 }
38 38
 class BlockStatement: Statement() {
39
-	val statements = ArrayList<Statement>()
39
+    val statements = ArrayList<Statement>()
40 40
     override fun <T> accept(visitor: SemVisitor<T>): T {
41 41
         return visitor.visitBlockStatement(this)
42 42
     }
43 43
 }
44 44
 abstract class Loop: Statement() {
45
-	var expression: Expr? = null
46
-	var mainBlock: BlockStatement? = null
47
-	var elseBlock: BlockStatement? = null
45
+    var expression: Expr? = null
46
+    var mainBlock: BlockStatement? = null
47
+    var elseBlock: BlockStatement? = null
48 48
 }
49 49
 class DoWhileStatement: Loop() {
50 50
     override fun <T> accept(visitor: SemVisitor<T>): T {
@@ -61,6 +61,7 @@ class ForStatement: Loop() {
61 61
 // A Decl is valid wherever a Statement can appear, plus some other places.
62 62
 abstract class Decl(var name: String? = null, var type: Type = Type.UNKNOWN): Statement() {
63 63
     var noref: Boolean = false
64
+    var emitted = false
64 65
 }
65 66
 class ModuleDecl: Decl() {
66 67
     val children = ArrayList<Decl>()
@@ -75,34 +76,41 @@ class OverloadSet(name: String): Decl(name) {
75 76
     }
76 77
 }
77 78
 class TypeDecl(name: String, type: Type): Decl(name, type) {
78
-	val children = ArrayList<Decl>()
79
+    val children = ArrayList<Decl>()
79 80
     override fun <T> accept(visitor: SemVisitor<T>): T {
80 81
         return visitor.visitTypeDecl(this)
81 82
     }
82 83
 }
83 84
 class VarDecl(name: String, type: Type = Type.UNKNOWN): Decl(name, type) {
84
-	var init: Expr? = null
85
-	var defaultInit = false
85
+    var init: Expr? = null
86
+    var defaultInit = false
86 87
     override fun <T> accept(visitor: SemVisitor<T>): T {
87 88
         return visitor.visitVarDecl(this)
88 89
     }
89 90
 }
90 91
 class FuncDecl(name: String): Decl(name) {
91
-	var args: List<FuncArg> = Collections.emptyList()
92
-	var body: BlockStatement = BlockStatement()
92
+    var args: List<FuncArg> = Collections.emptyList()
93
+    var body: BlockStatement = BlockStatement()
94
+    var rettype: Type = Type.UNKNOWN
93 95
     override fun <T> accept(visitor: SemVisitor<T>): T {
94 96
         return visitor.visitFuncDecl(this)
95 97
     }
98
+    val mangledName: String
99
+        get() {
100
+            return name ?: "unknown"
101
+        }
96 102
 }
97 103
 class FuncArg(name: String): Decl(name) {
98
-	var defaultValue: Expr? = null
104
+    var defaultValue: Expr? = null
99 105
     override fun <T> accept(visitor: SemVisitor<T>): T {
100 106
         return visitor.visitFuncArg(this)
101 107
     }
102 108
 }
103 109
 
104 110
 // An Expr is valid wherever a Statement can appear, plus some other places.
105
-abstract class Expr(var type: Type = Type.UNKNOWN): Statement() {}
111
+abstract class Expr(var type: Type = Type.UNKNOWN): Statement() {
112
+    var llvmRef: String = ""
113
+}
106 114
 class StringLiteral(val value: String): Expr(type = Type.STRING) {
107 115
     override fun <T> accept(visitor: SemVisitor<T>): T {
108 116
         return visitor.visitStringLiteral(this)
@@ -113,7 +121,6 @@ class IntLiteral(val value: Long, type: Type = Type.INTLITERAL): Expr(type) {
113 121
     override fun <T> accept(visitor: SemVisitor<T>): T {
114 122
         return visitor.visitIntLiteral(this)
115 123
     }
116
-
117 124
 }
118 125
 class FloatLiteral(val value: Double, type: Type = Type.NUMLITERAL): Expr(type) {
119 126
     override fun <T> accept(visitor: SemVisitor<T>): T {
@@ -125,6 +132,14 @@ class BoolLiteral(val value: Boolean): Expr(Type.BOOL) {
125 132
         return visitor.visitBoolLiteral(this)
126 133
     }
127 134
 }
135
+class UnitLiteral: Expr(Type.UNIT) {
136
+    override fun <T> accept(visitor: SemVisitor<T>): T {
137
+        return visitor.visitUnitLiteral(this)
138
+    }
139
+    companion object {
140
+        val INSTANCE = UnitLiteral()
141
+    }
142
+}
128 143
 class InvokeExpr(val name: String): Expr(type = Type.UNKNOWN) {
129 144
     override fun <T> accept(visitor: SemVisitor<T>): T {
130 145
         return visitor.visitInvokeExpr(this)

+ 16
- 1
jpetalc/src/main/java/org/ikeran/petal/semantic/SemanticVisitor.kt View File

@@ -256,10 +256,17 @@ class AntlrToSemanticVisitor(val errors: ErrorRecorder): PetalBaseVisitor<SemNod
256 256
 
257 257
     override fun visitFunc_definition(ctx: PetalParser.Func_definitionContext): SemNode? {
258 258
         val decl = FuncDecl(ctx.Identifier().text)
259
+        pushScope(decl)
259 260
         decl.args = (visit(ctx.arg_list()) as FuncArgs).args
261
+        if (ctx.type() == null) {
262
+            decl.rettype = Type.UNIT
263
+        } else {
264
+            decl.rettype = parseType(ctx.type())
265
+        }
260 266
         funcCount++
261 267
         decl.body = visit(ctx.block()) as BlockStatement
262 268
         funcCount--
269
+        popScope(decl)
263 270
         return decl
264 271
     }
265 272
 
@@ -320,7 +327,15 @@ class AntlrToSemanticVisitor(val errors: ErrorRecorder): PetalBaseVisitor<SemNod
320 327
     }
321 328
 
322 329
     override fun visitReturnstmt(ctx: PetalParser.ReturnstmtContext): SemNode? {
323
-        logger.error("not implemented"); return super.visitReturnstmt(ctx)
330
+        val ret = if (ctx.expression() != null) {
331
+            ReturnStatement(visit(ctx.expression()) as Expr)
332
+        } else {
333
+            ReturnStatement(UnitLiteral.INSTANCE)
334
+        }
335
+        // For some reason, this is required? It's bizarre.
336
+        ret.ast = ctx
337
+        ret.scope = scopes[scopes.size - 1]
338
+        return ret
324 339
     }
325 340
 
326 341
     override fun visitNamespace_declaration(ctx: PetalParser.Namespace_declarationContext): SemNode? {

+ 15
- 9
jpetalc/src/main/java/org/ikeran/petal/semantic/Types.kt View File

@@ -27,8 +27,8 @@ abstract class Type(
27 27
         val FLOAT = FloatType(size = 8)
28 28
         val FLOAT32 = FloatType(size = 4)
29 29
         val STRING = StringType()
30
-        val OBJECT = ClassType("Object", "petal:Object")
31
-        val IOBJECT = InterfaceType("IObject", "petal:IObject")
30
+        val OBJECT = ClassType("petal:Object")
31
+        val IOBJECT = InterfaceType("petal:IObject")
32 32
         val UNKNOWN = UnknownType()
33 33
         val ERROR = ErrorType()
34 34
         val UNIT = UnitType()
@@ -98,6 +98,8 @@ abstract class Type(
98 98
     open fun canBinOperator(op: String): Boolean {
99 99
         return false
100 100
     }
101
+
102
+    open val llvmRef: String = ""
101 103
 }
102 104
 
103 105
 val logger = LoggerFactory.getLogger("types")
@@ -119,6 +121,7 @@ class ErrorType(): Type("<error>") {
119 121
     override fun isValid(): Boolean { return false }
120 122
 }
121 123
 class UnitType(): Type("unit") {
124
+    override val llvmRef = "void"
122 125
     override fun equals(other: Any?): Boolean {
123 126
         return other is UnitType
124 127
     }
@@ -162,22 +165,27 @@ class BooleanType: Type("bool") {
162 165
     override fun canBinOperator(op: String): Boolean {
163 166
         return op == "and" || op == "or"
164 167
     }
168
+    override val llvmRef = "i1"
165 169
 }
166 170
 data class IntType(val signed: Boolean, val size: Int):
167 171
     Type("${if (signed) "" else "u"}int${size*8}"), Numeric {
168 172
     override fun canBinOperator(op: String): Boolean {
169 173
         return op == "+" || op == "-" || op == "*" || op == "/"
170 174
     }
175
+    override val llvmRef = "i${size * 8}"
171 176
 }
172 177
 data class FloatType(val size: Int) : Type("float${size * 8}"), Numeric {
173 178
     override fun canBinOperator(op: String): Boolean {
174 179
         return op == "+" || op == "-" || op == "*" || op == "/"
175 180
     }
181
+    override val llvmRef = if (size == 4) "float" else "double"
176 182
 }
177 183
 class StringType : Type("string") {
178 184
     override fun equals(other: Any?): Boolean {
179 185
         return other is StringType
180 186
     }
187
+    // TODO make an actual string type
188
+    override val llvmRef = "i8*"
181 189
 }
182 190
 data class VectorType(val member: Type) : Type("${member.name}[]") {}
183 191
 data class StaticArrayType(val member: Type, val length: Long): Type("${member.name}[${length}]") {}
@@ -198,20 +206,18 @@ data class FuncType(val returnType: Type, val args: List<Type>):
198 206
 
199 207
 // User-defined types
200 208
 open class UserDefinedType(name: String = ""): Type(name) {
209
+    // TODO change name to fully qualified name
201 210
     val attributes: List<Expr> = ArrayList<Expr>()
202 211
 }
203
-open class AggregateType(name: String = "") : Type(name)
204
-data class ClassType(val shortName: String, val qualifiedName: String):
205
-    AggregateType(qualifiedName) {
212
+open class AggregateType(name: String = "") : UserDefinedType(name)
213
+class ClassType(name: String): AggregateType(name) {
206 214
     /** Only class ProtoObject should have a null parent. Otherwise, if the parent's null, we'll set
207 215
     * it to Object. */
208 216
     var base: Type? = null
209 217
     val interfaces = ArrayList<InterfaceType>()
210 218
 }
211
-data class StructType(val shortName: String, val qualifiedName: String):
212
-    AggregateType(qualifiedName) {}
213
-data class InterfaceType(val shortName: String, val qualifiedName: String):
214
-    AggregateType(qualifiedName) {
219
+class StructType(name: String): AggregateType(name) {}
220
+class InterfaceType(name: String): AggregateType(name) {
215 221
     val interfaces = ArrayList<InterfaceType>()
216 222
 }
217 223
 data class EnumType(val shortName: String, val qualifiedName: String):

+ 22
- 0
jpetalc/src/test/java/org/ikeran/petal/TypeTest.kt View File

@@ -0,0 +1,22 @@
1
+package org.ikeran.petal;
2
+
3
+import org.junit.Test
4
+import org.ikeran.petal.semantic.*
5
+import org.junit.Assert
6
+
7
+public class TypeTest {
8
+    @Test
9
+    fun builtinTypesHaveLlvmRefs() {
10
+        Type.registerBuiltins()
11
+        for (p in Type.registry) {
12
+            val t = p.value
13
+            if (!t.isValid()) {
14
+                continue
15
+            }
16
+            if (t is UserDefinedType) {
17
+                continue
18
+            }
19
+            Assert.assertTrue("no llvm ref for builtin type " + t.name, t.llvmRef != "")
20
+        }
21
+    }
22
+}

+ 27
- 0
platforms.md View File

@@ -0,0 +1,27 @@
1
+# Supported platforms
2
+
3
+## amd64 / linux
4
+
5
+As of Petal 1.0, the Petal compiler will only support amd64 on Linux.
6
+
7
+Why?
8
+
9
+Petal aims to support desktop computers for GUI and command line applications. The overwhelming
10
+majority of desktop computers use amd64. Windows and OSX both cost money.
11
+
12
+## WebAssembly
13
+
14
+The target for Petal 1.1 is WebAssembly. The major blocking issue is creating our own garbage
15
+collector, and after that is a Javascript foreign function interface. When we create this, we'll
16
+split the standard library slightly according to what can be done in WebAssembly and what can't
17
+(which means system-level stuff and IO, mainly).
18
+
19
+One issue here is that strings in JS are UTF-16. We might want to create a type that makes it
20
+efficient to manage them. We might also need to ensure our template system can make it easier to
21
+handle various types of strings. Alternatively, we could make our existing string type support both
22
+UTF-8 and UTF-16. (How hard would that be?)
23
+
24
+## Future targets
25
+
26
+Future targets include Windows, AArch64, and MIPS. The primary development environment for Petal on
27
+Windows will likely be WINE. OSX will only be supported with donations.