Skip to content

Commit 5d144b6

Browse files
committed
std/comptime: add IncludeBytes (#128)
1 parent a34dc97 commit 5d144b6

File tree

8 files changed

+156
-13
lines changed

8 files changed

+156
-13
lines changed

std/comptime/embed.jule

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright 2026 The Jule Project Contributors. All rights reserved.
2+
// Use of this source code is governed by a BSD 3-Clause
3+
// license that can be found in the LICENSE file.
4+
5+
// Returns the contents of the file at path as a byte slice.
6+
// The file path is resolved relative to the file path of the package in which
7+
// the function is called. In other words, the file is searched for in the
8+
// directory of the package that contains the source file calling the function.
9+
// fn IncludeBytes(path: str): []byte

std/jule/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ It is also used by the official reference compiler julec and is developed in par
1515
- [`log`](./log): Elementary package for logs.
1616
- [`mod`](./mod): Module file parsing and module handling.
1717
- [`parser`](./parser): Parser. Makes syntax analysis, builds AST.
18+
- [`resources`](./resources): API for compile-time resources.
1819
- [`sema`](./sema): Semantic analyzer and HIR (High-Level Intermediate Representation) components.
1920
- [`token`](./token): Lexical analyzer. Segments Jule source code into tokens.
2021
- [`types`](./types): Elementary package for types.

std/jule/importer/importer.jule

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ use "std/jule/directive"
1010
use "std/jule/log"
1111
use "std/jule/mod"
1212
use "std/jule/parser"
13+
use "std/jule/resource"
1314
use "std/jule/sema"
1415
use "std/jule/token"
1516
use "std/os"
1617
use "std/os/filepath"
1718
use "std/strings"
19+
use "std/unsafe"
1820

1921
// Read buffer by file path.
2022
fn readBuff(path: str): []byte {
@@ -35,12 +37,26 @@ fn flatCompilerErr(text: str): log::Log {
3537
}
3638
}
3739

40+
struct file {
41+
path: str
42+
mut data: []byte
43+
}
44+
45+
impl resource::File for file {
46+
fn Path(*self): str { ret self.path }
47+
48+
fn Bytes(*self): []byte { ret self.data }
49+
50+
fn Text(*self): str { ret unsafe::StrFromBytes(self.data) }
51+
}
52+
3853
// Default importer for the reference Jule compiler.
3954
struct importer {
40-
mods: []&mod::Mod
41-
mod: &mod::Mod
42-
pkgs: []&sema::ImportInfo
43-
vars: []str
55+
mod: &mod::Mod
56+
mods: []&mod::Mod
57+
pkgs: []&sema::ImportInfo
58+
vars: []str
59+
files: map[str]resource::File
4460
}
4561

4662
impl sema::Importer for importer {
@@ -159,6 +175,20 @@ impl sema::Importer for importer {
159175
fn AllPackages(mut *self): []&sema::ImportInfo {
160176
ret self.pkgs
161177
}
178+
179+
fn GetFile(mut *self, path: str): resource::File {
180+
mut file, _ := self.files[path]
181+
ret file
182+
}
183+
184+
fn IncludeFile(mut *self, path: str): resource::File {
185+
mut data := os::ReadFileSync(path) else { ret nil }
186+
ret &file{path: path, data: data}
187+
}
188+
189+
fn IncludedFile(mut *self, mut file: resource::File) {
190+
self.files[file.Path()] = file
191+
}
162192
}
163193

164194
impl importer {
@@ -187,6 +217,7 @@ impl importer {
187217
// Returns new default Jule package importer by the compile information.
188218
fn New(info: CompileInfo): sema::Importer {
189219
mut imp := new(importer)
220+
imp.files = make(map[str]resource::File)
190221
imp.mods = [build::ModStdlib()]
191222
initVars(&imp.vars, info)
192223
ret imp

std/jule/resource/file.jule

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2026 The Jule Project Contributors. All rights reserved.
2+
// Use of this source code is governed by a BSD 3-Clause
3+
// license that can be found in the LICENSE file.
4+
5+
// A general file instance for compile-time embed files.
6+
trait File {
7+
// Returns path of the file.
8+
fn Path(*self): str
9+
10+
// Returns file content in bytes.
11+
// The return value may be mutable reference to the internal buffer,
12+
// and mutation is not allowed.
13+
fn Bytes(*self): []byte
14+
15+
// Returns file content in text.
16+
fn Text(*self): str
17+
}

std/jule/sema/api.jule

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ fn analyzePackage(mut files: []&ast::AST, mut importer: Importer, flags: int): (
7272
files: tables,
7373
flags: flags,
7474
meta: new(commonSemaMeta),
75+
importer: importer,
7576
}
7677

7778
// Use first table (so first file) for this.

std/jule/sema/builtin.jule

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
// Use of this source code is governed by a BSD 3-Clause
33
// license that can be found in the LICENSE file.
44

5+
use "std/conv"
56
use "std/fmt"
67
use "std/internal/jule/mod"
78
use "std/jule/ast"
89
use "std/jule/constant"
910
use "std/jule/log"
1011
use "std/jule/types"
12+
use "std/os/filepath"
1113

1214
// Type alias for built-in function callers.
1315
//
@@ -1290,6 +1292,71 @@ fn builtinCallerStdComptimeTypeAlias(mut e: &eval, mut fc: &ast::CallExpr, _: &V
12901292
ret buildVoidValue()
12911293
}
12921294

1295+
fn builtinCallerStdComptimeIncludeBytes(mut e: &eval, mut fc: &ast::CallExpr, _: &Value): &Value {
1296+
if len(fc.Args) > 1 {
1297+
e.pushErr(fc.Token, "passed more argument than expected to IncludeBytes")
1298+
e.pushSuggestion("call like; IncludeBytes(Path)")
1299+
ret nil
1300+
}
1301+
if len(fc.Args) == 0 {
1302+
e.pushErr(fc.Token, "identifier and type is missing for IncludeBytes")
1303+
e.pushSuggestion("call like; IncludeBytes(Path)")
1304+
ret nil
1305+
}
1306+
mut v := e.eval(fc.Args[0], evalDefault)
1307+
if v == nil {
1308+
ret nil
1309+
}
1310+
if !v.IsConst() || !v.Constant.IsStr() {
1311+
e.pushErr(fc.Args[0].Token, "expression must be constant and string")
1312+
ret nil
1313+
}
1314+
mut path := v.Constant.ReadStr()
1315+
if len(path) == 0 {
1316+
e.pushErr(fc.Args[0].Token, "empty path for IncludeBytes")
1317+
ret nil
1318+
}
1319+
if !filepath::IsAbs(path) {
1320+
packagefile := e.s.getCurrentFile()
1321+
packagedir := filepath::Dir(packagefile.File.Path)
1322+
path = filepath::Join(packagedir, path)
1323+
path = filepath::Abs(path) else {
1324+
e.pushErr(fc.Args[0].Token, "path handling failed: "+fmt::Sprint(error))
1325+
ret nil
1326+
}
1327+
}
1328+
mut file := e.s.importer.GetFile(path)
1329+
if file == nil {
1330+
file = e.s.importer.IncludeFile(path)
1331+
if file == nil {
1332+
e.pushErr(fc.Args[0].Token, "IncludeBytes cannot read file: "+conv::Quote(path))
1333+
ret nil
1334+
}
1335+
e.s.importer.IncludedFile(file)
1336+
}
1337+
data := file.Bytes()
1338+
mut bytes := &SliceExpr{
1339+
ElemType: primU8,
1340+
Elems: make([]&Value, 0, len(data)),
1341+
}
1342+
for _, b in data {
1343+
mut bc := constant::NewU64(u64(b))
1344+
bytes.Elems = append(bytes.Elems, &Value{
1345+
Type: &Type{Kind: primU8},
1346+
Constant: bc,
1347+
Model: bc,
1348+
})
1349+
}
1350+
ret &Value{
1351+
Type: &Type{
1352+
Kind: &Slice{
1353+
Value: primU8,
1354+
},
1355+
},
1356+
Model: bytes,
1357+
}
1358+
}
1359+
12931360
fn builtinCallerStdMemSizeOf(mut e: &eval, mut fc: &ast::CallExpr, _: &Value): &Value {
12941361
mut result := &Value{
12951362
Type: primUint,
@@ -1367,7 +1434,7 @@ fn builtinCallerStdIntegEmit(mut e: &eval, mut fc: &ast::CallExpr, mut v: &Value
13671434
}
13681435

13691436
if !argVal.IsConst() || !argVal.Constant.IsStr() {
1370-
e.pushErr(fc.Args[0].Token, "expression must be constant")
1437+
e.pushErr(fc.Args[0].Token, "expression must be constant and string")
13711438
ret nil
13721439
}
13731440

@@ -1496,6 +1563,7 @@ fn init() {
14961563
builtinFuncsStdComptime["File"] = &FuncIns{caller: builtinCallerStdComptimeFile}
14971564
builtinFuncsStdComptime["Files"] = &FuncIns{caller: builtinCallerStdComptimeFiles}
14981565
builtinFuncsStdComptime["TypeAlias"] = &FuncIns{caller: builtinCallerStdComptimeTypeAlias}
1566+
builtinFuncsStdComptime["IncludeBytes"] = &FuncIns{caller: builtinCallerStdComptimeIncludeBytes}
14991567

15001568
// Initialize built-in functions of the "std/integ" package.
15011569
builtinFuncsStdInteg["Emit"] = &FuncIns{

std/jule/sema/package.jule

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use "std/jule/ast"
66
use "std/jule/build"
77
use "std/jule/log"
88
use "std/jule/mod"
9+
use "std/jule/resource"
910
use "std/jule/token"
1011
use "std/os/filepath"
1112
use "std/strings"
@@ -35,18 +36,31 @@ trait Importer {
3536
// Path is the directory path of package to import.
3637
// Should return abstract syntax tree of package files.
3738
// Logs accepts as error.
38-
// Updated module to package's module if exist when updateMod is true.
39+
// Updates module to package's module if exist when updateMod is true.
3940
fn ImportPackage(mut *self, path: str, updateMod: bool): ([]&ast::AST, []log::Log)
4041

4142
// Invoked after the package is imported.
42-
// Sets module identitity of imported package to current module.
43+
// Sets module identitity of the imported package to current module.
44+
// If the current module is not registered, for example,
45+
// updated by the ImportPackage it will be registered.
4346
fn Imported(mut *self, mut &ImportInfo)
4447

4548
// Returns all imported packages.
4649
// The return value may be mutable reference to the internal buffer.
4750
// Packages should be ordered by FIFO; starting with the first deepest
4851
// imported package, and ending with the last imported package.
4952
fn AllPackages(mut *self): []&ImportInfo
53+
54+
// Returns File by path.
55+
// This function accepted as returns already included file.
56+
// If returns not-nil value, will be used instead of IncludeFile if possible.
57+
fn GetFile(mut *self, path: str): resource::File
58+
59+
// Includes the file by path, returns nil if failed.
60+
fn IncludeFile(mut *self, path: str): resource::File
61+
62+
// Invoked after the file is included.
63+
fn IncludedFile(mut *self, mut file: resource::File)
5064
}
5165

5266
fn findVarFileInPackage(mut files: []&SymTab, v: &Var): &SymTab {

std/jule/sema/sema.jule

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -306,12 +306,13 @@ const (
306306
// Semantic analyzer for tables.
307307
// Accepts tables as files of package.
308308
struct sema {
309-
errors: []log::Log
310-
files: []&SymTab // Package files.
311-
file: &SymTab // Current package file.
312-
flags: int
313-
meta: &commonSemaMeta
314-
step: int
309+
errors: []log::Log
310+
files: []&SymTab // Package files.
311+
file: &SymTab // Current package file.
312+
flags: int
313+
meta: &commonSemaMeta
314+
step: int
315+
importer: Importer
315316
}
316317

317318
impl Lookup for sema {
@@ -620,6 +621,7 @@ impl sema {
620621
files: imp.Package.Files,
621622
flags: self.flags,
622623
meta: self.meta,
624+
importer: self.importer,
623625
}
624626
s.setSemaFields()
625627
semas[i] = s

0 commit comments

Comments
 (0)