diff --git a/.gitignore b/.gitignore index 618fe6a..6b8dcf5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /cx *.c harpy +*.txt diff --git a/examples/dot_anon.cx b/examples/dot_anon.cx new file mode 100644 index 0000000..ed88f3d --- /dev/null +++ b/examples/dot_anon.cx @@ -0,0 +1,13 @@ +//# 67 +struct Foo { + int x; + static Foo new() => .{30} + static int get(Foo f) => f.x +} + +int get(Foo f) => f.x + +int main() { + Foo f = .new(); + return get(.{7}) + Foo.get(.new()) + f.x; +} diff --git a/examples/dot_anon2.cx b/examples/dot_anon2.cx new file mode 100644 index 0000000..065135e --- /dev/null +++ b/examples/dot_anon2.cx @@ -0,0 +1,9 @@ +//! name: Fernando\n +import std.string; +import std.io; + +int main() { + String name = .from("Fernando"); + printf("name: %s\n", name.ptr); + return 0; +} diff --git a/examples/dot_anon3.cx b/examples/dot_anon3.cx new file mode 100644 index 0000000..14aeec8 --- /dev/null +++ b/examples/dot_anon3.cx @@ -0,0 +1,10 @@ +//# 0 + +enum Foo { + Bar, +} + +int main() { + Foo f = .Bar; + return f; +} diff --git a/examples/foreach.cx b/examples/foreach.cx new file mode 100644 index 0000000..40c2be7 --- /dev/null +++ b/examples/foreach.cx @@ -0,0 +1,26 @@ +//# 0 +import std.array; +import std.stack; +import std.io; + +int main() { + Array arr = .new(4); + defer arr.free(); + + arr.push(60); + arr.push(7); + + int num = 0; + foreach k, n; arr { + printf("%d\n", k); + num += n; + } + + foreach &n; arr { + printf("%p\n", (void*) n); + } + + printf("Result: %d\n", num); + + return 0; +} diff --git a/examples/generics4.cx b/examples/generics4.cx index 8526ddd..11ecde9 100644 --- a/examples/generics4.cx +++ b/examples/generics4.cx @@ -35,6 +35,6 @@ int main() { Bar bar = {&f}; (*bar.s).val = 67; Cool.e("Fernando", 18); - Entry* entry = Entry.create("Fernando", 37); + Entry* entry = .create("Fernando", 37); return foo.val; } diff --git a/examples/map.cx b/examples/map.cx new file mode 100644 index 0000000..d48f578 --- /dev/null +++ b/examples/map.cx @@ -0,0 +1,26 @@ +//# 0 +import std.lib; +import std.io; + +struct Fun { + static U* map(T* data, u32 len, U(T) fn) { + U* result = malloc(sizeof(U) * len); + for (u32 i = 0; i < len; i++) result[i] = fn(data[i]); + return result; + } +} + +float toFloat(int x) => (float) x * 1.5F + +int main() { + int[5] nums = [1, 2, 3, 4, 5]; + + // float* floats = Fun.map(nums, 5, fn float(int x) => (float) x * 1.5f); + float* floats = Fun.map(nums, 5, toFloat); + defer free(floats); + + for (u32 i = 0; i < 5; i++) + printf("%f\n", floats[i]); + + return 0; +} diff --git a/examples/multi_var_decl.cx b/examples/multi_var_decl.cx new file mode 100644 index 0000000..b730c6c --- /dev/null +++ b/examples/multi_var_decl.cx @@ -0,0 +1,10 @@ +//# 67 +struct Foo { + float x, y; +} + +int main() { + int x, y = 2, 7; + Foo f = {y = 10F, x = 20F}; + return (x * (int)(f.x + f.y)) + y; +} diff --git a/examples/slice.cx b/examples/slice.cx new file mode 100644 index 0000000..919e712 --- /dev/null +++ b/examples/slice.cx @@ -0,0 +1,18 @@ +//! CX!\n.cx\n +import std.string; +import std.slice; +import std.io; + +int main() { + char* filename = "test.cx"; + size_t size = filename.length; + + Slice ext = filename[-3..size]; + if ext === ".cx" { + printf("CX!\n"); + } + + printf("%.*s\n", ext.length, ext.ptr); + + return 0; +} diff --git a/examples/std/array.cx b/examples/stdlib/array.cx similarity index 82% rename from examples/std/array.cx rename to examples/stdlib/array.cx index 79722d8..f066145 100644 --- a/examples/std/array.cx +++ b/examples/stdlib/array.cx @@ -5,16 +5,15 @@ import std.array; alias Value = int!ArrayError int main() { - Array arr = Array.new(1); + Array arr = .new(1); defer arr.free(); arr.push(60); arr.push(7); Value e = arr.get(2); - if !e.valid { + if !e.valid printf("Error: %d\n", e.error); - } Value v1 = arr.get(0); Value v2 = arr.get(1); diff --git a/examples/std/box.cx b/examples/stdlib/box.cx similarity index 68% rename from examples/std/box.cx rename to examples/stdlib/box.cx index c204c16..8c56a98 100644 --- a/examples/std/box.cx +++ b/examples/stdlib/box.cx @@ -3,6 +3,6 @@ import std.box; import std.io; int main() { - Box val = Box.of(67); + Box val = .of(67); return val.unwrap(); } diff --git a/examples/std/file.cx b/examples/stdlib/file.cx similarity index 100% rename from examples/std/file.cx rename to examples/stdlib/file.cx diff --git a/examples/std/hashmap.cx b/examples/stdlib/hashmap.cx similarity index 84% rename from examples/std/hashmap.cx rename to examples/stdlib/hashmap.cx index d60128a..6d6e008 100644 --- a/examples/std/hashmap.cx +++ b/examples/stdlib/hashmap.cx @@ -3,12 +3,12 @@ import std.hashmap; import std.io; int main() { - HashMap map = HashMap.new(16); - defer map.delete(); + HashMap map = .new(16); + defer map.free(); map.set("age", 18); map.set("year", 2026); - map.set("port", 8080); + map.set("port", 8080); Box age = map.get("age"); if !age.isEmpty() diff --git a/examples/std/hello.cx b/examples/stdlib/hello.cx similarity index 100% rename from examples/std/hello.cx rename to examples/stdlib/hello.cx diff --git a/examples/stdlib/slice.cx b/examples/stdlib/slice.cx new file mode 100644 index 0000000..96f4ba3 --- /dev/null +++ b/examples/stdlib/slice.cx @@ -0,0 +1,20 @@ +//! view[0]: 20\nfoo: 3.000000\n +import std.slice; +import std.io; + +Slice foo() { + float[] f = [0f, 2f, 3f, 1f, 4f]; + return f[2...3]; // heap +} + +int main() { + int[5] arr = [10, 20, 30, 40, 50]; + Slice view = arr[1..2]; // Slice.of(arr, 1, 2); + printf("view[0]: %d\n", view.get(0)); + + Slice f = foo(); + defer f.free(); + printf("foo: %f\n", f.get(0)); + + return 0; +} diff --git a/examples/std/stack.cx b/examples/stdlib/stack.cx similarity index 100% rename from examples/std/stack.cx rename to examples/stdlib/stack.cx diff --git a/examples/std/str.cx b/examples/stdlib/str.cx similarity index 73% rename from examples/std/str.cx rename to examples/stdlib/str.cx index f354217..d78bcbf 100644 --- a/examples/std/str.cx +++ b/examples/stdlib/str.cx @@ -3,8 +3,8 @@ import std.string; import std.io; int main() { - String s1 = String.from("Fernando"); - String s2 = String.from(" the "); + String s1 = .from("Fernando"); + String s2 = .from(" the "); defer s1.free(); defer s2.free(); diff --git a/src/backend/codegen.d b/src/backend/codegen.d index 4f69751..376f2da 100644 --- a/src/backend/codegen.d +++ b/src/backend/codegen.d @@ -14,8 +14,11 @@ final class CodeGen private: Program program; TypeRegistry types; + TypeResolver resolver; + TypeExpr actualType; TypeExpr fnType; + bool isFnStatic; bool genHeaderFile; string headerFile; @@ -150,6 +153,10 @@ private: RawStmt n = cast(RawStmt) node; return emit(indent("/* raw block */", ind) ~ n.code, 0); + case NodeKind.VarDecl: + data ~= compileVarDecl(cast(VarDecl)node, 0); + return; + default: emit("/* invalid decl */", ind); return; @@ -277,6 +284,46 @@ private: emit("}", ind); return ""; + case NodeKind.ForEachStmt: + ForEachStmt fe = cast(ForEachStmt) node; + + string sname = fe.value.type_expr.toStr(); + string value = compileExpr(fe.value); + + FnDecl iter = resolver.findMethod(sname, "iter"); + string iterator = iter.type_expr.toString(); + + string temp = format("__it%d", tmp++); + string it = format("%s %s = %s_iter(&%s);", iterator, temp, sname, value); + + bool isRef = fe.v.kind == NodeKind.UnaryExpr; + string val = isRef ? compileExpr((cast(UnaryExpr) fe.v).val) : compileExpr(fe.v); + + // writeln(sname); + // writeln(value); + // writeln(iterator); + // writeln(temp); + // writeln(it); + + emit(it, ind); + emit(format("for (;%s.offset < %s.length; %s.offset++)", temp, temp, temp), ind); + emit(format("{"), ind); + if (fe.k !is null) + emit(format("size_t %s = %s.offset;", compileExpr(fe.k), temp), ind); + emit(format("__typeof__(%s(%s.ptr)) %s = %s(%s.ptr[%s.offset]);", + isRef ? "" : "*", temp, val, isRef ? "&" : "", temp, temp), ind+4); + foreach (Node n; fe.body) + emit(compileStmt(n, ind), ind); + emit(format("}"), ind); + + // Iterator __it1 = arr.iter(); OK + // for (; __it1.offset < __it1.length; __it1.offset++) { + // int n = __it1.ptr[__it1.offset]; + // num += n; + // } + + return ""; + default: return indent("/* invalid stmt */", ind); } @@ -402,8 +449,18 @@ private: BinaryExpr binary = cast(BinaryExpr) node; string left = compileExpr(binary.left); string right = compileExpr(binary.right); - if (isString(binary.left.type_expr) && isString(binary.right.type_expr) && binary.op == TokenKind.EEEquals) - return format("strcmp(%s, %s) == 0", left, right); + if (binary.op == TokenKind.EEEquals) + { + if (isString(binary.left.type_expr) && isString(binary.right.type_expr)) + return format("strcmp(%s, %s) == 0", left, right); + if (isStruct(binary.left.type_expr)) + { + string name = binary.left.type_expr.toStr(); + FnDecl fn = resolver.findMethod(name, "cmp"); + if (fn) + return format("%s_cmp(&%s, %s)", name, left, right); + } + } return format("%s %s %s", left, getOp(binary.op), right); case NodeKind.UnaryExpr: @@ -516,8 +573,24 @@ private: return format("(%s)%s", n.type_expr.toString(), compileExpr(n.expr)); case NodeKind.IndexExpr: - IndexExpr idx = cast(IndexExpr) node; - return format("%s[%s]", compileExpr(idx.value), compileExpr(idx.idx)); + IndexExpr idxExpr = cast(IndexExpr) node; + string val = compileExpr(idxExpr.value); + // writeln(idxExpr.idx); + // writeln(idxExpr.value); + // writeln("val: ", val); + if (RangeExpr range = cast(RangeExpr) idxExpr.idx) + { + string left = compileExpr(range.left); + string right = compileExpr(range.right); + + if (range.left.kind == NodeKind.UnaryExpr) + left = right ~ left; + + return format("Slice_%s_%s(%s, %s, %s)", + idxExpr.value.type_expr.toStr(), range.isCopy ? "copyOf" : "of", + val, left, right); + } + return format("%s[%s]", val, compileExpr(idxExpr.idx)); case NodeKind.GroupExpr: return "(" ~ compileExpr((cast(GroupExpr) node).val) ~ ")"; @@ -585,6 +658,14 @@ private: string compileMemberExpr(MemberExpr node) { + if (node.right.kind == NodeKind.StructLit) + { + // string expr = compileExpr(node.right); + // writeln(expr); + // return expr; + return compileExpr(node.right); + } + TypeExpr type = node.left.type_expr; string typeName, id; bool isArrow; @@ -632,6 +713,11 @@ private: id = format("tmp_%d", tmp++); emit(format("%s %s = %s;", m.right.type_expr, id, member), 4); } + } + else if (CallExpr c = cast(CallExpr) node.left) + { + id = format("tmp_%d", tmp++); + emit(format("%s %s = %s;", c.type_expr, id, compileExpr(c)), 4); } if (id != "") { @@ -871,7 +957,7 @@ private: public: this(Program program, TypeRegistry types, bool[string] staticFunctions, bool noHeader, bool genHeaderFile, - string headerFile, ImportResolverContext* context, bool isCpp) + string headerFile, ImportResolverContext* context, bool isCpp, TypeResolver resolver) { this.program = program; this.types = types; @@ -879,6 +965,7 @@ public: this.genHeaderFile = genHeaderFile; this.headerFile = headerFile; this.context = context; + this.resolver = resolver; if (noHeader) return; cxHeader ~= ` #ifndef __CLANG_STDINT_H diff --git a/src/env.d b/src/env.d index f2999df..8b72460 100644 --- a/src/env.d +++ b/src/env.d @@ -1,4 +1,4 @@ module env; -const string COMPILER_VERSION = "0.2.0"; +const string COMPILER_VERSION = "0.2.1"; const string GITHUB_REPO = "https://github.com/FernandoTheDev/cx.git"; diff --git a/src/frontend/lexer/lexer.d b/src/frontend/lexer/lexer.d index f88173c..76438ea 100644 --- a/src/frontend/lexer/lexer.d +++ b/src/frontend/lexer/lexer.d @@ -29,6 +29,8 @@ private: "__is": TokenKind.Is, "__type": TokenKind.Type, "__typename": TokenKind.TypeName, + + "foreach": TokenKind.ForEach, "default": TokenKind.Default, "switch": TokenKind.Switch, "case": TokenKind.Case, @@ -68,12 +70,14 @@ private: "}": TokenKind.RBrace, "[": TokenKind.LBracket, "]": TokenKind.RBracket, + + ".": TokenKind.Dot, + "..": TokenKind.Range, "...": TokenKind.Ellipsis, ",": TokenKind.Comma, ":": TokenKind.Colon, ";": TokenKind.SemiColon, - ".": TokenKind.Dot, "@": TokenKind.At, "+": TokenKind.Plus, @@ -96,6 +100,8 @@ private: "&&": TokenKind.And, "||": TokenKind.Or, "?": TokenKind.Question, + "??": TokenKind.QQuestion, + "?.": TokenKind.QDot, "+=": TokenKind.PLUSEquals, "-=": TokenKind.MINUSEquals, @@ -247,7 +253,7 @@ private: void lexDecimal(String buffer, out bool isDouble) { lexNumber(buffer); - if (check('.')) + if (!isAtEnd(1) && check('.') && isNumeric(future(1))) { isDouble = true; buffer ~= [advance()]; diff --git a/src/frontend/lexer/token.d b/src/frontend/lexer/token.d index 37735fc..823d387 100644 --- a/src/frontend/lexer/token.d +++ b/src/frontend/lexer/token.d @@ -8,6 +8,7 @@ enum TokenKind : ubyte Include, // keywords + ForEach, Is, Type, TypeName, @@ -64,6 +65,7 @@ enum TokenKind : ubyte SemiColon, // ; Dot, // . At, // @ + Range, // .. Ellipsis, // ... Plus, // + @@ -85,7 +87,6 @@ enum TokenKind : ubyte SHREquals, // >>= Equals, // = - Arrow, // => EEquals, // == EEEquals, // === @@ -98,6 +99,8 @@ enum TokenKind : ubyte And, // && Or, // || Question, // ? + QQuestion, // ?? + QDot, // ?. BITLeft, // << BITRight, // >> diff --git a/src/frontend/parser/ast.d b/src/frontend/parser/ast.d index acfafce..00b350c 100644 --- a/src/frontend/parser/ast.d +++ b/src/frontend/parser/ast.d @@ -10,6 +10,7 @@ import std.format : format; enum NodeKind : ubyte { Program, // 1 2 + Multi, // 1 2 NumericLit, // 1 2 DoubleLit, // 1 2 @@ -34,6 +35,7 @@ enum NodeKind : ubyte IsExpr, // 1 2 TTypeExpr, // 1 2 TernaryExpr, // 1 2 + RangeExpr, // 1 2 IncludeHeader, // 1 2 VarDecl, // 1 2 @@ -57,6 +59,7 @@ enum NodeKind : ubyte RawStmt, // 1 2 SwitchStmt, // 1 2 CaseStmt, // 1 2 + ForEachStmt, // 1 2 } abstract class Node @@ -1716,6 +1719,105 @@ class TernaryExpr : Node } } +class Multi : Node +{ + Node[] body; + + this(Node[] body) + { + super(NodeKind.Multi); + this.body = body; + } + + override void print(uint indent = 0) + { + iprint(indent, "Multi"); + } + + override Multi dup() + { + // + return new Multi(dupArr(body)); + } + + override void subGeneric(string[] names, TypeExpr[] types) + { + type_expr = subGenericType(type_expr, names, types); + subGenericArr(body, names, types); + } +} + +class RangeExpr : Node +{ + Node left, right; + bool isCopy; + + this(Node left, Node right, bool isCopy, Position pos) + { + super(NodeKind.RangeExpr, pos); + this.left = left; + this.right = right; + this.isCopy = isCopy; + } + + override void print(uint indent = 0) + { + iprint(indent, "RangeExpr"); + } + + override RangeExpr dup() + { + return new RangeExpr(left.dup(), right.dup(), isCopy, pos); + } + + override void subGeneric(string[] names, TypeExpr[] types) + { + type_expr = subGenericType(type_expr, names, types); + // subGenericArr(body, names, types); + // left.subGeneric(names, types); + // right.subGeneric(names, types); + left.type_expr = subGenericType(left.type_expr, names, types); + right.type_expr = subGenericType(right.type_expr, names, types); + } +} + +class ForEachStmt : Node +{ + Node k, v, value; + Node[] body; + + this(Node k, Node v, Node value, Node[] body, Position pos) + { + super(NodeKind.ForEachStmt, pos); + this.k = k; + this.v = v; + this.value = value; + this.body = body; + } + + override void print(uint indent = 0) + { + iprint(indent, "ForEachStmt"); + } + + override ForEachStmt dup() + { + return new ForEachStmt(k is null ? null : k.dup(), v.dup(), value.dup(), dupArr(body), pos); + } + + override void subGeneric(string[] names, TypeExpr[] types) + { + // subGenericArr(body, names, types); + // left.subGeneric(names, types); + // right.subGeneric(names, types); + if (k !is null) + k.type_expr = subGenericType(k.type_expr, names, types); + v.type_expr = subGenericType(v.type_expr, names, types); + value.type_expr = subGenericType(value.type_expr, names, types); + subGenericArr(body, names, types); + } +} + pragma(inline, true) private void iprint(uint indent, string s) { diff --git a/src/frontend/parser/parse_decl.d b/src/frontend/parser/parse_decl.d index b3d65d1..d8c66f8 100644 --- a/src/frontend/parser/parse_decl.d +++ b/src/frontend/parser/parse_decl.d @@ -20,35 +20,59 @@ public: Node parseVarDecl(TypeExpr texpr, Token name, bool consumeSemiColon = false) { - // T NAME = VAL - Node value; + // T NAME, NAME2, ... = VAL, VAL2, ...; + if (texpr is null || name is null || name.kind != TokenKind.Id) + return new VarDecl("err", Node.init, false, new TypeExprNamed("/*err*/"), Position.init); + + Token[] names = [name]; + p.vars[name.s] = texpr; + + // coleta nomes adicionais: T a, b, c + while (p.check(TokenKind.Comma)) + { + p.advance(); // consome ',' + Token n = p.consume(TokenKind.Id, "Expected identifier after ','."); + names ~= n; + p.vars[n.s] = texpr; + } + + Node[] values; + if (p.check(TokenKind.SemiColon)) - value = null; + // sem inicializador: preenche com null pra cada nome + foreach (n; names) + values ~= null; else { p.consume(TokenKind.Equals, "Expected '='."); - value = p.parseExpr.parse(); + values ~= p.parseExpr.parse(); + + while (p.check(TokenKind.Comma)) + { + p.advance(); + values ~= p.parseExpr.parse(); + } + + if (values.length != names.length) + p.err.error(p.getPos(name.pos, values[$-1].pos), + format("Expected %d values but got %d.", names.length, values.length)); } - + if (consumeSemiColon) p.match(TokenKind.SemiColon); + + Node[] decls; + // writeln(values); + foreach (i, n; names) + decls ~= new VarDecl(n.s, i >= values.length ? null : values[i], false, texpr, p.getPos(texpr.pos, n.pos)); - if (texpr is null || name is null || name.kind != TokenKind.Id) - { - // writeln("VAR: ", name); - return new VarDecl("err", Node.init, false, new TypeExprNamed("/*err*/"), Position.init); - } - // writeln("Type: ", texpr); - // writeln("Type Pos: ", texpr.pos); - // writeln("Name: ", name.kind); - // writeln("Name.s: ", name.s); - // writeln("Value: ", value, "\n"); - p.vars[name.s] = texpr; - // writeln("New Var: ", p.vars[name.s]); - return new VarDecl(name.s, value, false, texpr, p.getPos(texpr.pos, name.pos)); + if (decls.length == 1) + return decls[0]; + + return new Multi(decls); } - Node parseFnDecl(TypeExpr retType, Token name, bool isStatic, string baseName = "") + Node parseFnDecl(TypeExpr retType, Token name, bool isStatic, string baseName = "", string[] genericT = []) { TypeExpr[string] vars = p.vars; p.vars = (TypeExpr[string]).init; @@ -56,11 +80,19 @@ public: p.consume(TokenKind.LParen, "Expected '('."); FnArg[] args; ubyte flags; + bool isGeneric; + + pragma(inline, true); + bool exists(string n) { + return (genericT.map!(x => x == n).array).length > 0; + } while (!p.check(TokenKind.RParen)) { TypeExpr type = p.parseType.parse(); Token argName = p.consume(TokenKind.Id, "Expected an identifier."); + if (exists(argName.s)) + isGeneric = true; Node val = null; if (p.match(TokenKind.Equals)) val = p.parseExpr.parse(); @@ -98,6 +130,9 @@ public: p.ctx.statics[fnName] = true; } + if (flags & NodeFlags.Overload && isGeneric) + p.err.error(name.pos, "You cannot use overloading on a generic function."); + p.vars = vars; return new FnDecl(fnName, args, body, retType, name.pos, flags); } @@ -138,13 +173,18 @@ public: TypeExpr type = p.parseType.parse(); Token name = p.consume(TokenKind.Id, "Expected an 'ID'."); if (p.check(TokenKind.LParen)) - { - functions ~= cast(FnDecl)parseFnDecl(type, name, isStatic, genericT.length > 0 ? "" : sname.s); + functions ~= cast(FnDecl)parseFnDecl(type, name, isStatic, genericT.length > 0 ? "" : sname.s, + genericT); // if (functions[$-1].flags & NodeFlags.Overload) // functions[$-1].name = sname.s ~ "_" ~ functions[$-1].name; - } else - fields ~= cast(VarDecl)parseVarDecl(type, name, true); + { + Node var = parseVarDecl(type, name, true); + if (var.kind == NodeKind.VarDecl) + fields ~= cast(VarDecl) var; + else + fields ~= cast(VarDecl[])(cast(Multi) var).body; + } } } diff --git a/src/frontend/parser/parse_expr.d b/src/frontend/parser/parse_expr.d index 48d3077..7beb24d 100644 --- a/src/frontend/parser/parse_expr.d +++ b/src/frontend/parser/parse_expr.d @@ -98,7 +98,7 @@ public: Token name = p.consume(TokenKind.Id, "Expected an 'ID' after the type."); if (p.check(TokenKind.LParen)) return p.parseDecl.parseFnDecl(type, name, false); - if (p.check(TokenKind.Equals) || p.check(TokenKind.SemiColon)) + if (p.check(TokenKind.Equals) || p.check(TokenKind.SemiColon) || p.check(TokenKind.Comma)) return p.parseDecl.parseVarDecl(type, name); } if (label) @@ -185,6 +185,10 @@ public: case TokenKind.LParen: return parseCastOrNode(tk.pos); + case TokenKind.Dot: + // .call() | .member + return parseMemberExpr(null); + default: // tk.print(); p.err.error(tk.pos, "An expression is expected."); @@ -292,6 +296,7 @@ public: || p.future(TokenKind.SemiColon, 1) || p.future(TokenKind.Dot, 1) || p.future(TokenKind.LParen, 1) + || p.future(TokenKind.Comma, 1) ) { // writeln("OK"); @@ -398,12 +403,23 @@ public: p.advance(); // consome '(' val = parseCallExpr(val); // reusa a função existente, empacota como CallExpr(val, args) } - return new MemberExpr(left, val, p.getPos(left.pos, val.pos)); + return new MemberExpr(left, val, p.getPos(left is null ? null : left.pos, val.pos)); + } + + Node parseIndex() + { + Node left = parse(); + // p.previous().print(); + // p.peek().print(); + bool isCopy = p.peek().kind == TokenKind.Ellipsis; + if (p.match(TokenKind.Range) || p.match(TokenKind.Ellipsis)) + return new RangeExpr(left, parse(), isCopy, left.pos); + return left; } Node parseIndexExpr(Node left) { - Node idx = parse(); + Node idx = parseIndex(); Position end = p.consume(TokenKind.RBracket, "Expected ']'.").pos; return new IndexExpr(left, idx, p.getPos(left.pos, end)); } diff --git a/src/frontend/parser/parse_stmt.d b/src/frontend/parser/parse_stmt.d index 689a1cf..2bdbc59 100644 --- a/src/frontend/parser/parse_stmt.d +++ b/src/frontend/parser/parse_stmt.d @@ -176,7 +176,7 @@ public: { if (p.check(TokenKind.RBrace)) break; - Node node = p.parseIntern(); + Node node = p.parseIntern()[0]; if (node.kind == NodeKind.ContinueOrBreakStmt || node.kind == NodeKind.ReturnStmt) close = true; if (node.kind == NodeKind.VarDecl) @@ -186,6 +186,21 @@ public: return new CaseStmt(value, hasVar, body, pos); } + Node parseForEachStmt(Position pos) + { + Node k, v, value; + v = p.parseExpr.parse(); + if (p.match(TokenKind.Comma)) + { + k = v; + v = p.parseExpr.parse(); + } + p.consume(TokenKind.SemiColon, "Expected ';'."); + value = p.parseExpr.parse(); + Node[] body = parseBody(); + return new ForEachStmt(k, v, value, body, pos); + } + Node parse() { Token tk = p.advance(); @@ -226,6 +241,9 @@ public: case TokenKind.Default: return parseCaseStmt(tk.pos, tk.kind == TokenKind.Default); + case TokenKind.ForEach: + return parseForEachStmt(tk.pos); + default: return new IdentExpr("null", new TypeExprNamed("void", tk.pos), tk.pos); } diff --git a/src/frontend/parser/parser.d b/src/frontend/parser/parser.d index 8844ee4..a6ba8bd 100644 --- a/src/frontend/parser/parser.d +++ b/src/frontend/parser/parser.d @@ -130,6 +130,7 @@ class Parser case NodeKind.ContinueOrBreakStmt: case NodeKind.GotoStmt: case NodeKind.ImportStmt: + case NodeKind.Multi: return true; default: return false; @@ -160,6 +161,7 @@ class Parser case TokenKind.Switch: case TokenKind.Case: case TokenKind.Default: + case TokenKind.ForEach: return true; default: return false; @@ -181,16 +183,18 @@ class Parser } } - Node parseIntern() + Node[] parseIntern() { - Node node; + Node[] node; if (isDecl()) - node = parseDecl.parse(); + node ~= parseDecl.parse(); else if (isStmt()) - node = parseStmt.parse(); + node ~= parseStmt.parse(); else - node = parseExpr.parse(Precedence.Low, true); - checkSemiColon(node); + node ~= parseExpr.parse(Precedence.Low, true); + checkSemiColon(node[0]); + if (node[0].kind == NodeKind.Multi) + node ~= (cast(Multi)node[0]).body; return node; } diff --git a/src/frontend/type_resolve.d b/src/frontend/type_resolve.d index eb83c4a..490d35c 100644 --- a/src/frontend/type_resolve.d +++ b/src/frontend/type_resolve.d @@ -1,6 +1,7 @@ module frontend.type_resolve; import frontend; +import utils; import std.format; import std.stdio; @@ -35,10 +36,13 @@ final class TypeResolver private: TypeRegistry types; TypeExpr currentSelfType; // tipo (sem ponteiro) da struct dona do método atual + Diagnostics err; // nome da struct -> declaração completa, pra achar campos e métodos StructDecl[string] structs; TypeExpr[string] functions; + TypeExpr reference = null; + TypeExpr[][string] functionsArgs; void collectDecls(Program program) { @@ -70,7 +74,7 @@ private: return null; } - FnDecl findMethod(string structName, string methodName) + public FnDecl findMethod(string structName, string methodName) { if (auto s = structName in structs) foreach (fn; (*s).functions) @@ -180,21 +184,53 @@ private: resolveExprType(tn.right, scp); return tn.left.type_expr; + case NodeKind.RangeExpr: + RangeExpr range = cast(RangeExpr) n; + resolveExprType(range.left, scp); + resolveExprType(range.right, scp); + return range.type_expr; + default: // já vêm com type_expr setado no próprio construtor return n.type_expr; } } + bool isStructLit(Node node) + { + if (node is null) + return false; + if (node.kind == NodeKind.StructLit) + return true; + if (MemberExpr m = cast(MemberExpr) node) + return m.left is null && isStructLit(m.right); + return false; + } + TypeExpr resolveMemberExpr(MemberExpr m, Scope scp) { + // .call() | .member | .{} + // if (node.left is null) + // { + // if (originalRef !is null && reference !is null) + // node.left = new IdentExpr(originalRef, reference, node.pos); + // } + if (m.left is null) + { + // não da pra resolver o tipo + if (m.right.kind == NodeKind.StructLit) + return resolveExprType(m.right, scp); + // if (m.right.kind == NodeKind.CallExpr || m.right.kind == NodeKind.IdentExpr) + // return TypeExpr.init; + if (reference !is null) + m.left = new IdentExpr(reference.toStr(), reference, m.pos); + } + // caso especial: right é uma chamada de método -> a.metodo(...) if (m.right.kind == NodeKind.CallExpr) { CallExpr call = cast(CallExpr) m.right; - foreach (arg; call.args) - resolveExprType(arg, scp); - + TypeExpr leftType = resolveExprType(m.left, scp); m.left.type_expr = leftType; @@ -208,6 +244,22 @@ private: if (call.callee.kind == NodeKind.IdentExpr) methodName = (cast(IdentExpr) call.callee).val; + TypeExpr re = reference; + reference = null; + string callee = format("%s_%s", sName, methodName); + + foreach (i, ref Node arg; call.args) + { + if (callee in functionsArgs) + { + reference = i >= functionsArgs[callee].length ? null : functionsArgs[callee][i]; + if (reference !is null && isStructLit(arg)) + arg = new CastExpr(arg, reference, arg.pos); + } + resolveExprType(arg, scp); + } + reference = re; + FnDecl fn = findMethod(sName, methodName); if (fn is null) // fallback @@ -274,13 +326,25 @@ private: TypeExpr resolveCallExpr(CallExpr c, Scope scp) { - foreach (arg; c.args) + TypeExpr re = reference; + reference = null; + string callee = c.callee.kind == NodeKind.IdentExpr ? (cast(IdentExpr)c.callee).val : ""; + + foreach (i, ref Node arg; c.args) + { + if (callee in functionsArgs) + { + reference = i >= functionsArgs[callee].length ? null : functionsArgs[callee][i]; + if (reference !is null && isStructLit(arg)) + arg = new CastExpr(arg, reference, arg.pos); + } resolveExprType(arg, scp); + } + reference = re; if (c.callee.kind != NodeKind.IdentExpr) resolveExprType(c.callee, scp); else { - string callee = (cast(IdentExpr)c.callee).val; // writeln("Callee: ", callee); // writeln(functions, "\n"); if (TypeExpr* t = callee in functions) @@ -303,6 +367,9 @@ private: VarDecl v = cast(VarDecl) n; if (v.val !is null) { + TypeExpr re = reference; + reference = v.type_expr; + scope (exit) reference = re; resolveExprType(v.val, scp); if (v.val.type_expr is null) v.val.type_expr = v.type_expr; } @@ -310,7 +377,10 @@ private: return; case NodeKind.ReturnStmt: - resolveExprType((cast(ReturnStmt) n).val, scp); + ReturnStmt ret = cast(ReturnStmt) n; + if (isStructLit(ret.val) && reference !is null) + ret.val = new CastExpr(ret.val, reference, ret.val.pos); + resolveExprType(ret.val, scp); return; case NodeKind.IfStmt: @@ -361,6 +431,52 @@ private: case NodeKind.ContinueOrBreakStmt: return; + case NodeKind.ForEachStmt: + ForEachStmt fe = cast(ForEachStmt) n; + Scope inner = new Scope(scp); + + if (fe.k !is null) + resolveExprType(fe.k, inner); + + if (fe.v !is null) + { + resolveExprType(fe.v, inner); + if (UnaryExpr un = cast(UnaryExpr) fe.v) + { + if (un.op != TokenKind.BITAnd) + { + err.error(fe.v.pos, "Unexpected operator."); + goto end; + } + if (un.val.kind != NodeKind.IdentExpr) + { + err.error(fe.v.pos, "Invalid value for foreach."); + goto end; + } + } + } + + if (fe.value !is null) + { + resolveExprType(fe.value, inner); + if (!isStruct(fe.value.type_expr)) + { + err.error(fe.value.pos, "It is only possible to iterate over structs."); + goto end; + } + + string sname = fe.value.type_expr.toStr(); + if (!findMethod(sname, "iter")) + { + err.error(fe.value.pos, + "The target struct cannot be iterated because it does not contain an iterator."); + goto end; + } + } + end: + resolveBody(fe.body, inner); + return; + default: // CallExpr, MemberExpr, AssignStmt, UnaryExpr usados como statement resolveExprType(n, scp); @@ -381,14 +497,21 @@ private: if (ownerName !is null && !(fn.flags & NodeFlags.Static)) scp.declare("self", new TypeExprPointer(*types.get(ownerName))); foreach (arg; fn.args) + { + functionsArgs[fn.name] ~= arg.type_expr; scp.declare(arg.name, arg.type_expr); + } + TypeExpr re = reference; + reference = fn.type_expr; + scope (exit) reference = re; resolveBody(fn.body, scp); } public: - this(TypeRegistry types) + this(TypeRegistry types, Diagnostics err) { this.types = types; + this.err = err; } void resolve(Program program) diff --git a/src/main.d b/src/main.d index a70b6b7..c220ac9 100644 --- a/src/main.d +++ b/src/main.d @@ -206,7 +206,10 @@ int main(string[] argv) // faz duas passagens pra resolução completa generic.resolve(program); generic.resolve(program); - new TypeResolver(registry).resolve(program); + + TypeResolver resolver = new TypeResolver(registry, err); + resolver.resolve(program); + check_diagnostic(err); program.body = ResolveSymbols.resolve(err, ctx, program.body); check_diagnostic(err); @@ -216,7 +219,7 @@ int main(string[] argv) string fileh = output ~ (cpp ? ".hpp" : ".h"); string filec = output ~ (cpp ? ".cpp" : ".c"); - string[2] src = new CodeGen(program, registry, ctx.statics, noHeader, genHeader, fileh, ctx, cpp).compile(); + string[2] src = new CodeGen(program, registry, ctx.statics, noHeader, genHeader, fileh, ctx, cpp, resolver).compile(); check_diagnostic(err); write(filec, src[0]); diff --git a/src/utils.d b/src/utils.d index 72c3e71..46f37e7 100644 --- a/src/utils.d +++ b/src/utils.d @@ -1,6 +1,7 @@ module utils; import frontend.parser.ast : Node; +import frontend.type_expr; import core.stdc.stdlib : exit; import std.exception; @@ -41,3 +42,12 @@ string clearNameMangling(string name) buff ~= name[i]; return buff; } + +bool isStruct(TypeExpr type) +{ + if (TypeExprUser p = cast(TypeExprUser) type) + return p.kind == TypeExprKind.Struct; + if (TypeExprGeneric p = cast(TypeExprGeneric) type) + return true; + return false; +} diff --git a/std/array.cx b/std/array.cx index 872bca4..653d115 100644 --- a/std/array.cx +++ b/std/array.cx @@ -1,3 +1,4 @@ +import std.iterator; import std.lib; enum ArrayError { @@ -32,6 +33,8 @@ struct Array { return self.data[offset]; } + Iterator iter() => .{self.data, 0, self.size} + void free() { free(self.data); diff --git a/std/hashmap.cx b/std/hashmap.cx index 2de4c12..82ace7a 100644 --- a/std/hashmap.cx +++ b/std/hashmap.cx @@ -53,6 +53,8 @@ u32 murmur3(char* key, u32 len, u32 seed) { return h1; } +// Fernando +// 91839181 u32 hashKey(char* key) { return murmur3(key, key.length, 0x9747b28c); } @@ -77,30 +79,27 @@ struct HashMap { u32 size; static HashMap new(u32 cap) { - Entry** buckets = calloc(cap, sizeof(Entry*)); - return (HashMap) {buckets, cap, 0}; + return .{calloc(cap, sizeof(Entry*)), cap, 0}; } - u32 bucketIndex(char* key) { - return hashKey(key) % self.cap; - } + u32 bucketIndex(char* key) => hashKey(key) % self.cap void set(char* key, V value) { u32 idx = self.bucketIndex(key); Entry* curr = self.buckets[idx]; while curr != null { - if strcmp(curr.key, key) == 0 { + if curr.key === key { curr.value = value; return; } curr = curr.next; } - Entry* entry = Entry.create(key, value); + Entry* entry = .create(key, value); entry.next = self.buckets[idx]; self.buckets[idx] = entry; - self.size = self.size + 1; + self.size++; } Box get(char* key) { @@ -108,20 +107,17 @@ struct HashMap { Entry* curr = self.buckets[idx]; while curr != null { - if curr.key === key - return Box.of(curr.value); + if curr.key === key + return .of(curr.value); curr = curr.next; } - return Box.empty(); + return .empty(); } - bool has(char* key) { - Box res = self.get(key); - return !res.isEmpty(); - } + bool has(char* key) => !self.get(key).isEmpty() - void delete() { + void free() { u32 i = 0; while i < self.cap { Entry* curr = self.buckets[i]; @@ -130,7 +126,7 @@ struct HashMap { free(curr); curr = next; } - i = i + 1; + i++; } free(self.buckets); } diff --git a/std/iterator.cx b/std/iterator.cx new file mode 100644 index 0000000..b52d1ac --- /dev/null +++ b/std/iterator.cx @@ -0,0 +1,5 @@ +struct Iterator { + T* ptr; + size_t offset; + size_t length; +} diff --git a/std/slice.cx b/std/slice.cx new file mode 100644 index 0000000..48a4358 --- /dev/null +++ b/std/slice.cx @@ -0,0 +1,25 @@ +import std.string; +import std.lib; + +struct Slice { + T* ptr; + u32 length; + bool owned; + + static Slice of(T* data, u32 start, u32 end) => .{data + start, end - start, false} + + static Slice copyOf(T* data, u32 start, u32 end) { + u32 len = end - start; + T* newData = malloc(sizeof(T) * len); + memcpy(newData, data + start, sizeof(T) * len); + return .{newData, len, true}; + } + + bool cmp(T* other) => memcmp(self.ptr, other, self.length) == 0 + T get(u32 idx) => self.ptr[idx] + + void free() { + if self.owned + free(self.ptr); + } +} diff --git a/std/string.cx b/std/string.cx index 707141f..fb95a73 100644 --- a/std/string.cx +++ b/std/string.cx @@ -7,20 +7,9 @@ struct String size_t length; bool owner; - static String from(char* str) - { - return (String) {str, str.length, false}; - } - - static String new(size_t cap) - { - return (String) {malloc(cap), cap, true}; - } - - char* data() - { - return self.ptr; - } + static String from(char* str) => .{str, str.length, false} + static String new(size_t cap) => .{malloc(cap), cap, true} + char* data() => self.ptr char* concat(char* other) overload {