diff --git a/examples/unions.cal b/examples/unions.cal new file mode 100644 index 0000000..2b887fb --- /dev/null +++ b/examples/unions.cal @@ -0,0 +1,17 @@ +include "cores/select.cal" +include "std/io.cal" + +union MyUnion + cell + Array +end + +"MyUnion.sizeof = " printlstr MyUnion.sizeof printdec new_line + +let MyUnion foo + +65 foo ! +foo @ printdec new_line + +"Hello, world!\n" foo a< +foo printstr diff --git a/source/app.d b/source/app.d index d682e25..fa52c3d 100644 --- a/source/app.d +++ b/source/app.d @@ -26,6 +26,7 @@ Flags: executable file (set by default) -na - Disables the -a flag --version - Shows the callisto version + -dp - Prints parser output Backends: rm86 - Real mode x86 and MS-DOS @@ -52,6 +53,7 @@ int main(string[] args) { bool runFinal = true; CompilerBackend backend = new BackendLinux86(); bool doDebug; + bool debugParser; for (size_t i = 1; i < args.length; ++ i) { if (args[i][0] == '-') { @@ -152,6 +154,10 @@ int main(string[] args) { doDebug = true; break; } + case "-dp": { + debugParser = true; + break; + } default: { stderr.writefln("Unknown flag '%s'", args[i]); return 1; @@ -176,6 +182,14 @@ int main(string[] args) { string[] included; auto nodes = ParseFile(file); + if (debugParser) { + foreach (ref node ; nodes) { + writeln(node); + } + + return 0; + } + auto compiler = new Compiler(); compiler.backend = backend; compiler.backend.org = org; diff --git a/source/backends/linux86.d b/source/backends/linux86.d index bc4a707..46a4f7a 100644 --- a/source/backends/linux86.d +++ b/source/backends/linux86.d @@ -608,4 +608,32 @@ class BackendLinux86 : CompilerBackend { output ~= format("jmp __while_%d_condition\n", currentLoop); } + + override void CompileUnion(UnionNode node) { + size_t maxSize = 0; + + if (node.name in types) { + Error(node.error, "Type '%s' already exists", node.name); + } + + string[] unionTypes; + + foreach (ref type ; node.types) { + if (unionTypes.canFind(type)) { + Error(node.error, "Union type '%s' defined twice", type); + } + unionTypes ~= type; + + if (type !in types) { + Error(node.error, "Type '%s' doesn't exist", type); + } + + if (types[type].size > maxSize) { + maxSize = types[type].size; + } + } + + types[node.name] = Type(maxSize); + NewConst(format("%s.sizeof", node.name), cast(long) maxSize); + } } diff --git a/source/backends/rm86.d b/source/backends/rm86.d index c055b22..5dcf600 100644 --- a/source/backends/rm86.d +++ b/source/backends/rm86.d @@ -569,4 +569,32 @@ class BackendRM86 : CompilerBackend { output ~= format("jmp __while_%d_condition\n", currentLoop); } + + override void CompileUnion(UnionNode node) { + size_t maxSize = 0; + + if (node.name in types) { + Error(node.error, "Type '%s' already exists", node.name); + } + + string[] unionTypes; + + foreach (ref type ; node.types) { + if (unionTypes.canFind(type)) { + Error(node.error, "Union type '%s' defined twice", type); + } + unionTypes ~= type; + + if (type !in types) { + Error(node.error, "Type '%s' doesn't exist", type); + } + + if (types[type].size > maxSize) { + maxSize = types[type].size; + } + } + + types[node.name] = Type(maxSize); + NewConst(format("%s.sizeof", node.name), cast(long) maxSize); + } } diff --git a/source/compiler.d b/source/compiler.d index 1a1d468..67adb68 100644 --- a/source/compiler.d +++ b/source/compiler.d @@ -39,6 +39,7 @@ class CompilerBackend { abstract void CompileEnum(EnumNode node); abstract void CompileBreak(WordNode node); abstract void CompileContinue(WordNode node); + abstract void CompileUnion(UnionNode node); final void Error(Char, A...)(ErrorInfo error, in Char[] fmt, A args) { ErrorBegin(error); @@ -157,6 +158,7 @@ class Compiler { case NodeType.Struct: backend.CompileStruct(cast(StructNode) inode); break; case NodeType.Const: backend.CompileConst(cast(ConstNode) inode); break; case NodeType.Enum: backend.CompileEnum(cast(EnumNode) inode); break; + case NodeType.Union: backend.CompileUnion(cast(UnionNode) inode); break; default: { backend.Error(inode.error, "Unimplemented node '%s'", inode.type); } @@ -181,7 +183,8 @@ class Compiler { case NodeType.Requires: case NodeType.Struct: case NodeType.Const: - case NodeType.Enum: { + case NodeType.Enum: + case NodeType.Union: { header ~= node; break; } diff --git a/source/parser.d b/source/parser.d index b22f968..7a20058 100644 --- a/source/parser.d +++ b/source/parser.d @@ -26,7 +26,8 @@ enum NodeType { Struct, Const, Enum, - Restrict + Restrict, + Union } class Node { @@ -361,6 +362,26 @@ class RestrictNode : Node { override string toString() => format("restrict %s", ver); } +class UnionNode : Node { + string name; + string[] types; + + this(ErrorInfo perror) { + type = NodeType.Union; + error = perror; + } + + override string toString() { + auto ret = format("union %s\n", name); + + foreach (ref type ; types) { + ret ~= format(" %s\n", type); + } + + return ret ~ "end"; + } +} + class ParserError : Exception { this() { super("", "", 0); @@ -838,6 +859,29 @@ class Parser { return ret; } + Node ParseUnion() { + auto ret = new UnionNode(GetError()); + parsing = NodeType.Union; + + Next(); + Expect(TokenType.Identifier); + ret.name = tokens[i].contents; + Next(); + Expect(TokenType.Identifier); + + while (true) { + if ((tokens[i].type == TokenType.Identifier) && (tokens[i].contents == "end")) { + break; + } + + ret.types ~= tokens[i].contents; + Next(); + Expect(TokenType.Identifier); + } + + return ret; + } + Node ParseStatement() { switch (tokens[i].type) { case TokenType.Integer: { @@ -859,6 +903,7 @@ class Parser { case "const": return ParseConst(); case "enum": return ParseEnum(); case "restrict": return ParseRestrict(); + case "union": return ParseUnion(); default: return new WordNode(GetError(), tokens[i].contents); } }