From 08086633e7c6a3ee00d8e79cff5a9cffff5cd564 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Mon, 16 Sep 2024 12:43:57 +0300 Subject: [PATCH] jule: add the export directive --- src/julec/README.md | 4 +++- src/julec/obj/cxx/expr.jule | 2 +- src/julec/obj/cxx/ident.jule | 14 ++++++++++-- src/julec/obj/cxx/object.jule | 22 +++++++++++++----- src/julec/obj/cxx/scope.jule | 2 +- std/jule/build/directive.jule | 1 + std/jule/build/log.jule | 1 + std/jule/sema/directive.jule | 42 +++++++++++++++++++++++++++++++++++ std/jule/sema/eval.jule | 2 ++ std/jule/sema/type2.jule | 2 +- 10 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/julec/README.md b/src/julec/README.md index 1013bc792..0c4d977e8 100644 --- a/src/julec/README.md +++ b/src/julec/README.md @@ -75,4 +75,6 @@ Actually, JuleC just generates C++ code for now. - **(3)** Scope part of deadcoe elimination optimizations, should be executed after other optimizations. Because other optimzations may will change code structure, so some scope deadcode elimination cases may occur. This possible optimizations cannot catched by scope optimizer if scope deadcode elimination optimizations applied before other independent middle-end optimizations. -- **(4)** For C/C++ IRs, include linked headers before the API. Otherwise it may cause compilation errors. For example, on Windows, `winsock2.h` must be included before `windows.h`. In a case where the API includes `windows.h` it is against this to later include `winsock2.h`. \ No newline at end of file +- **(4)** For C/C++ IRs, include linked standard headers before the API. Otherwise it may cause compilation errors. For example, on Windows, `winsock2.h` must be included before `windows.h`. In a case where the API includes `windows.h` it is against this to later include `winsock2.h`. + +- **(5)** Since user defined headers may use exported definitions of Jule, they must be imported after Jule declarations. Otherwise, compilation errors may occur. \ No newline at end of file diff --git a/src/julec/obj/cxx/expr.jule b/src/julec/obj/cxx/expr.jule index 2f4298ba2..e6d979518 100644 --- a/src/julec/obj/cxx/expr.jule +++ b/src/julec/obj/cxx/expr.jule @@ -221,7 +221,7 @@ impl exprCoder { fn operatorOverloadingUnary(mut &self, mut &s: &StructIns, op: TokenId, mut e: compExprModel) { const Unary = true - f := obj::FindOperator(s, op, Unary) + mut f := obj::FindOperator(s, op, Unary) if f == nil { panic("unary operator overloading is not exist, this is an implementation mistake") } diff --git a/src/julec/obj/cxx/ident.jule b/src/julec/obj/cxx/ident.jule index 5fad8ec3c..39eb35992 100644 --- a/src/julec/obj/cxx/ident.jule +++ b/src/julec/obj/cxx/ident.jule @@ -83,7 +83,7 @@ impl identCoder { identCoder.writeIdentTo(buf, ident) } - static fn func(mut &buf: StrBuilder, &f: &Fn) { + static fn func(mut &buf: StrBuilder, mut &f: &Fn) { match { | f.Binded: buf.WriteStr(f.Ident) @@ -96,10 +96,15 @@ impl identCoder { buf.WriteStr("static_") } } + export := obj::FindDirective(f.Directives, Directive.Export) + if export != nil { + buf.WriteStr(export.Args[0].Kind) + ret + } identCoder.toOut(buf, f.Ident, uintptr(f)) } - static fn funcIns(mut &buf: StrBuilder, &f: &FnIns) { + static fn funcIns(mut &buf: StrBuilder, mut &f: &FnIns) { if f.IsBuiltin() { // Do not use [identCoder.writeIdentTo] for this. // Built-in functions are always ASCII. @@ -180,6 +185,11 @@ impl identCoder { | v.Scope != nil: identCoder.toLocal(buf, v.Token.Row, v.Token.Column, v.Ident) |: + export := obj::FindDirective(v.Directives, Directive.Export) + if export != nil { + buf.WriteStr(export.Args[0].Kind) + ret + } identCoder.toOut(buf, v.Ident, uintptr(v)) } } diff --git a/src/julec/obj/cxx/object.jule b/src/julec/obj/cxx/object.jule index ad6df5b8e..c381755e3 100644 --- a/src/julec/obj/cxx/object.jule +++ b/src/julec/obj/cxx/object.jule @@ -459,25 +459,31 @@ impl ObjectCoder { self.write("#define __JULE_DISABLE__SAFETY\n") } - // Include binded libraries here, before the API header. + // Include binded standard library headers here, before the API header. // See developer reference (4). - self.links() + self.links(true) self.write("\n\n#include \"") self.write(build::PathApi) self.write("\"\n\n") } - fn links(mut &self) { + fn links(mut &self, std: bool) { for _, used in self.ir.Used { match { | !used.Binded: continue | build::IsStdHeaderPath(used.Path): + if !std { + continue + } self.write("#include ") self.write(used.Path) self.write("\n") | build::IsValidHeaderExt(path::Ext(used.Path)): + if std { + continue + } self.write("#include \"") self.write(used.Path) self.write("\"\n") @@ -542,7 +548,7 @@ impl ObjectCoder { fn structureDestructor(mut &self, mut &s: &StructIns) { // Dispose method must be non-static const Static = false - disposeMethod := s.FindMethod("Dispose", Static) + mut disposeMethod := s.FindMethod("Dispose", Static) mut disposed := FuncPattern.Dispose(disposeMethod) // Call destructor if implemented. if !disposed { @@ -1065,6 +1071,12 @@ impl ObjectCoder { self.write("\n\n") self.declPos = self.Buf.Len() self.globalDecls() + + // Include user-defined libraries here. + // See developer reference (5). + self.write("\n\n") + self.links(false) + self.write("\n\n") } fn structureMethods(mut &self, mut &s: &StructIns) { @@ -1167,7 +1179,7 @@ impl ObjectCoder { fn pushInit(mut &self, mut &pkg: &Package) { obj::IterFiles(pkg, fn(mut &file: &SymbolTable) { - for _, f in file.Funcs { + for (_, mut f) in file.Funcs { if f.Ident == build::InitFn { self.indent() identCoder.func(self.Buf, f) diff --git a/src/julec/obj/cxx/scope.jule b/src/julec/obj/cxx/scope.jule index 8a4a24d7a..edef3cf08 100644 --- a/src/julec/obj/cxx/scope.jule +++ b/src/julec/obj/cxx/scope.jule @@ -508,7 +508,7 @@ impl scopeCoder { fn operatorOverloadingAssign(mut &self, mut &s: &sema::StructIns, mut &l: &sema::OperandExprModel, mut &r: &sema::OperandExprModel, op: lex::TokenId) { const Unary = false - f := obj::FindOperator(s, op, Unary) + mut f := obj::FindOperator(s, op, Unary) if f == nil { panic("binary operator overloading is not exist, this is an implementation mistake") } diff --git a/std/jule/build/directive.jule b/std/jule/build/directive.jule index 85a097d31..ce198e8ce 100644 --- a/std/jule/build/directive.jule +++ b/std/jule/build/directive.jule @@ -16,6 +16,7 @@ enum Directive: str { Namespace: "namespace", Deprecated: "deprecated", Test: "test", + Export: "export", } // Reports whether directive is top-directive. diff --git a/std/jule/build/log.jule b/std/jule/build/log.jule index 64fe996d5..b59e3e689 100644 --- a/std/jule/build/log.jule +++ b/std/jule/build/log.jule @@ -250,6 +250,7 @@ enum LogMsg: str { InvalidComptimeIter: `comptime iterations can only be range iteration`, InvalidComptimeTypeMatchExpr: `comptime type-match expressions can take only type declarations`, WrongRetForward: "function return forwaring is wrong\n want (@)\n have (@)", + ExportedUsedAsAnonymous: `define @ is exported for backend so you cannot use as anonymous function`, // Suggestions. ExpectedIdentifier: `write an identifier because identifier expected`, diff --git a/std/jule/sema/directive.jule b/std/jule/sema/directive.jule index e59bf4963..fe9be7e68 100644 --- a/std/jule/sema/directive.jule +++ b/std/jule/sema/directive.jule @@ -81,6 +81,7 @@ impl directiveChecker { ret } + // remove quotes d.Args[0].Kind = arg.Kind[1:len(arg.Kind)-1] // Push relevant directives. @@ -133,6 +134,7 @@ impl directiveChecker { ret } + // remove quotes d.Args[0].Kind = arg.Kind[1:len(arg.Kind)-1] } @@ -157,6 +159,44 @@ impl directiveChecker { } } + fn checkExport(mut self, mut &d: &ast::Directive) { + match type self.o { + | &Fn: + f := (&Fn)(self.o) + if f.Binded || f.IsInit() || f.IsEntryPoint() || f.IsMethod() || len(f.Generics) > 0 { + self.s.pushErr(d.Tag, LogMsg.UnsupportedDirective, d.Tag.Kind) + ret + } + | &Var: + v := (&Var)(self.o) + if v.Binded || v.Constant { + self.s.pushErr(d.Tag, LogMsg.UnsupportedDirective, d.Tag.Kind) + } + |: + self.s.pushErr(d.Tag, LogMsg.UnsupportedDirective, d.Tag.Kind) + ret + } + if len(d.Args) > 1 { + self.s.pushErr(d.Args[1], LogMsg.ArgumentOverflow, d.Tag.Kind) + ret + } + if len(d.Args) < 1 { + self.s.pushErr(d.Tag, LogMsg.MissingExpr) + ret + } + arg := d.Args[0] + if arg.Id != TokenId.Lit { + self.s.pushErr(arg, LogMsg.InvalidSyntax) + ret + } + if arg.Kind[0] != '"' { + self.s.pushErr(arg, LogMsg.InvalidSyntax) + ret + } + // remove quotes + d.Args[0].Kind = arg.Kind[1:len(arg.Kind)-1] + } + fn checkDirective(mut self, mut &d: &ast::Directive) { match d.Tag.Kind { | Directive.Cdef: @@ -169,6 +209,8 @@ impl directiveChecker { self.checkDeprecated(d) | Directive.Test: self.checkTest(d) + | Directive.Export: + self.checkExport(d) | Directive.Build | Directive.Pass: self.s.pushErr(d.Tag, LogMsg.UnsupportedDirective, d.Tag.Kind) diff --git a/std/jule/sema/eval.jule b/std/jule/sema/eval.jule index dc274389d..684107491 100644 --- a/std/jule/sema/eval.jule +++ b/std/jule/sema/eval.jule @@ -3087,6 +3087,8 @@ impl eval { } if !f.Decl.Statically && f.Decl.IsMethod() { self.s.pushErr(expr.Token, LogMsg.MethodNotInvoked) + } else if findDirective(f.Decl.Directives, Directive.Export) != nil { + self.s.pushErr(expr.Token, LogMsg.ExportedUsedAsAnonymous, f.Decl.Ident) } else { f.AsAnon = true } diff --git a/std/jule/sema/type2.jule b/std/jule/sema/type2.jule index 15a0c4fb7..d25d25acb 100644 --- a/std/jule/sema/type2.jule +++ b/std/jule/sema/type2.jule @@ -19,7 +19,7 @@ use ast for std::jule::ast::{ TupleExpr, FnCallExpr, } -use std::jule::build::{LogMsg, Logf} +use std::jule::build::{Directive, LogMsg, Logf} use lit for std::jule::constant::lit use std::jule::lex::{Token, Ident, IsIgnoreIdent, IsAnonIdent} use std::internal::strings::{StrBuilder}