From ab8671a45288dab1b0ed97561e388d6e9d30d5e5 Mon Sep 17 00:00:00 2001 From: Lukas Neubert Date: Thu, 21 Dec 2023 17:17:37 +0100 Subject: [PATCH] refactor(gen): handling of comptime pseudo vars (#156) - remove from Prefs and set them during codegen - set only on usage - properly print them in failed asserts --- cli/bait.bt | 1 - docs/docs.md | 37 +++++++++++---------- lib/bait/ast/ast.bt | 4 +-- lib/bait/checker/expr.bt | 6 ++-- lib/bait/gen/js/comptime.bt | 55 +++++++++++++++++++++++++++++++ lib/bait/gen/js/expr.bt | 20 ++--------- lib/bait/gen/js/jsgen.bt | 4 +++ lib/bait/gen/js/stmt.bt | 3 ++ lib/bait/parser/expr.bt | 6 ++-- lib/bait/preference/preference.bt | 11 ------- tests/other/comptime_vars_test.bt | 22 +++++++++++++ tests/other/number_test.bt | 4 +-- tests/other/ptr_deref_test.bt | 4 +-- tests/other/u8_test.bt | 4 +-- 14 files changed, 120 insertions(+), 61 deletions(-) create mode 100644 lib/bait/gen/js/comptime.bt create mode 100644 tests/other/comptime_vars_test.bt diff --git a/cli/bait.bt b/cli/bait.bt index 9e2bc096..2d8dd92b 100644 --- a/cli/bait.bt +++ b/cli/bait.bt @@ -29,7 +29,6 @@ fun main() { args := os.user_args() timers.start('PREFS') mut prefs := preference.parse_args(args) - prefs.set_comptime_vars() timers.set_show(prefs.show_timings) timers.show('PREFS') diff --git a/docs/docs.md b/docs/docs.md index f270a195..f5ef95cc 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -1,9 +1,10 @@ + # Bait Documentation -> Documentation of the standard library is :construction: +> Documentation of the standard library is work in progress :construction: ## Entry Point The entry point of any program is the `main` function. It is automatically called when the program starts. @@ -424,28 +425,30 @@ struct FooBar { | ----------- | ------------------------------------------- | ------ | | `@required` | The field must be initialized with a value. | _none_ | + ## Conditional Compilation ### Compile Time Pseudo Variables -Bait supports a few pseudo variables of `string` type. -They are replaced with the actual value during compilation. - -| Variable | Description | Example | -| ------------ | ----------------------------------- | ----------------------- | -| `$PKG` | Name of the current package. | `main` | -| `$FILE` | Relative path of the source file. | `lib/builtin/file.bt` | -| `$ABS_FILE` | Absolute path of the source file. | `/path/to/file.bt` | -| `$LINE` | Line number of it's appearance. | `123` | -| `$FILE_LINE` | Relative path followed by the line. | `tests/my_test.bt:27` | -| `$FUN` | Name of the current function. | `test_read_line` | -| `$BAITEXE` | Absolut path to the Bait compiler. | `/path/to/bait/bait.js` | -| `$BAITDIR` | Directory where the compiler is in. | `/path/to/bait` | -| `$BAITHASH` | Short commit hash of the compiler. | `5e7fd6e` | +Bait supports a few pseudo variables, which are replaced by their actual values during compilation. +They are all of the type `string`. + +| Variable | Description | Example | +| ------------ | --------------------------------- | ----------------------- | +| `$PKG` | Current package name | `main` | +| `$ABS_FILE` | Absolute source file path | `/path/to/file.bt` | +| `$FILE` | Relative source file path | `lib/builtin/file.bt` | +| `$LINE` | Line number where it is used | `123` | +| `$FILE_LINE` | Relative path and the line | `tests/my_test.bt:27` | +| `$FUN` | Current function name | `test_read_line` | +| `$BAITEXE` | Absolut path to the Bait compiler | `/path/to/bait/bait.js` | +| `$BAITDIR` | Directory of the compiler | `/path/to/bait` | +| `$BAITHASH` | The compiler's short commit hash | `5e7fd6e` | They are useful for running external tools or debugging. For example: ```bait eprintln('error in file ${$FILE}, line ${$LINE}, function ${$PKG}.${$FUN}') ``` + ## Global Variables While the use of global variables is discouraged, they are important in some cases. diff --git a/lib/bait/ast/ast.bt b/lib/bait/ast/ast.bt index 532fc341..e37b1f01 100644 --- a/lib/bait/ast/ast.bt +++ b/lib/bait/ast/ast.bt @@ -7,7 +7,7 @@ import bait.errors type Stmt := AssertStmt | AssignStmt | ConstDecl | EnumDecl | ExprStmt | ForLoop | ForClassicLoop | ForInLoop | FunDecl | GlobalDecl | InterfaceDecl | ReturnStmt | StructDecl | TypeDecl | LoopControlStmt | EmptyStmt | MatchExpr | IfExpr -type Expr := AnonFun | ArrayInit | AsCast | BoolLiteral | CallExpr | CharLiteral | CompTimeVar | EnumVal | FloatLiteral | HashExpr | Ident | IfExpr | IndexExpr | InfixExpr | IntegerLiteral | MatchExpr | MapInit | ParExpr | PrefixExpr | SelectorExpr | StringLiteral | StringInterLiteral | StructInit | TypeOf | EmptyExpr +type Expr := AnonFun | ArrayInit | AsCast | BoolLiteral | CallExpr | CharLiteral | ComptimeVar | EnumVal | FloatLiteral | HashExpr | Ident | IfExpr | IndexExpr | InfixExpr | IntegerLiteral | MatchExpr | MapInit | ParExpr | PrefixExpr | SelectorExpr | StringLiteral | StringInterLiteral | StructInit | TypeOf | EmptyExpr pub struct AssertStmt { pub: @@ -237,7 +237,7 @@ pub: pos token.Pos } -pub struct CompTimeVar{ +pub struct ComptimeVar{ pub: name string pos token.Pos diff --git a/lib/bait/checker/expr.bt b/lib/bait/checker/expr.bt index 6cfb9dfb..1cc6d9aa 100644 --- a/lib/bait/checker/expr.bt +++ b/lib/bait/checker/expr.bt @@ -18,8 +18,8 @@ fun (mut c Checker) expr(expr ast.Expr) ast.Type { return c.call_expr(expr) } else if expr is ast.CharLiteral { return ast.U8_TYPE - } else if expr is ast.CompTimeVar { - return c.comp_time_var(expr) + } else if expr is ast.ComptimeVar { + return c.comptime_var(expr) } else if expr is ast.EnumVal { return c.enum_val(expr) } else if expr is ast.FloatLiteral { @@ -155,7 +155,7 @@ const SUPPORTED_COMPTIME_VARS := [ 'BAITHASH', ] -fun (mut c Checker) comp_time_var(node ast.CompTimeVar) ast.Type { +fun (mut c Checker) comptime_var(node ast.ComptimeVar) ast.Type { if not SUPPORTED_COMPTIME_VARS.contains(node.name) { c.error('unsupported comptime var "${node.name}"', node.pos) } diff --git a/lib/bait/gen/js/comptime.bt b/lib/bait/gen/js/comptime.bt new file mode 100644 index 00000000..7ab755dc --- /dev/null +++ b/lib/bait/gen/js/comptime.bt @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2023-present Lukas Neubert +// SPDX-License-Identifier: MPL-2.0 +package js + +import os +import bait.ast +import bait.token + +fun (mut g Gen) comptime_var(node ast.ComptimeVar){ + g.write('from_js_string("') + g.write(g.get_comptime_val(node.name, node.pos)) + g.write('")') +} + +fun (mut g Gen) get_comptime_val(name string, pos token.Pos) string { + return match name { + // Dynamic + 'PKG' { g.pkg } + 'ABS_FILE' { os.abs_path(g.path).replace('\\', '\\\\') } + 'FILE' { g.path.replace('\\', '\\\\') } + 'LINE' { pos.line.str() } + 'FILE_LINE' { + file := g.get_comptime_val('FILE', pos) + line := g.get_comptime_val('LINE', pos) + '${file}:${line}' + } + 'FUN' { g.cur_fun.name } + // Cached + 'BAITEXE' { g.comptime_baitexe() } + 'BAITDIR' { g.comptime_baitdir() } + 'BAITHASH' { g.comptime_baithash() } + } +} + +fun (mut g Gen) comptime_baitexe() string { + if g.baitexe.length == 0 { + g.baitexe = os.executable().replace('\\', '\\\\') + } + return g.baitexe +} + +fun (mut g Gen) comptime_baitdir() string { + if g.baitdir.length == 0 { + g.baitdir = os.dir(g.comptime_baitexe()).trim_right('\\') + } + return g.baitdir +} + +fun (mut g Gen) comptime_baithash() string { + if g.baithash.length == 0 { + bd := g.comptime_baitdir() + g.baithash = os.exec('git -C ${bd} rev-parse --short HEAD').stdout.trim_space() + } + return g.baithash +} diff --git a/lib/bait/gen/js/expr.bt b/lib/bait/gen/js/expr.bt index c6b86a60..7a01e899 100644 --- a/lib/bait/gen/js/expr.bt +++ b/lib/bait/gen/js/expr.bt @@ -21,8 +21,8 @@ fun (mut g Gen) expr(expr ast.Expr) { g.call_expr(expr) } else if expr is ast.CharLiteral { g.char_literal(expr) - } else if expr is ast.CompTimeVar { - g.comp_time_var(expr) + } else if expr is ast.ComptimeVar { + g.comptime_var(expr) } else if expr is ast.EnumVal { g.enum_val(expr) } else if expr is ast.FloatLiteral { @@ -131,22 +131,6 @@ fun (mut g Gen) char_literal(node ast.CharLiteral) { g.write('")') } -fun (mut g Gen) comp_time_var(node ast.CompTimeVar){ - g.write('from_js_string("') - match node.name{ - 'PKG' {g.write(g.pkg)} - 'FILE' {g.write(g.path.replace('\\', '\\\\'))} - 'ABS_FILE' {g.write(os.abs_path(g.path).replace('\\', '\\\\'))} - 'LINE' {g.write('${node.pos.line}')} - 'FILE_LINE' {g.write('${g.path.replace('\\', '\\\\')}:${node.pos.line}')} - 'FUN' {g.write(g.cur_fun.name)} - 'BAITEXE' {g.write(g.pref.baitexe)} - 'BAITDIR' {g.write(g.pref.baitdir)} - 'BAITHASH' {g.write(g.pref.baithash)} - } - g.write('")') -} - fun (mut g Gen) enum_val(node ast.EnumVal) { g.write(js_name(node.name) + '.' + node.val) } diff --git a/lib/bait/gen/js/jsgen.bt b/lib/bait/gen/js/jsgen.bt index d427c2b1..8ded71a7 100644 --- a/lib/bait/gen/js/jsgen.bt +++ b/lib/bait/gen/js/jsgen.bt @@ -63,6 +63,10 @@ mut: is_for_loop_head bool is_lhs_assign bool is_array_map_set bool + // Cached comptime variables + baitexe string + baitdir string + baithash string } pub fun gen(files []ast.File, table ast.Table, pref preference.Prefs) string { diff --git a/lib/bait/gen/js/stmt.bt b/lib/bait/gen/js/stmt.bt index ec62f684..200b142e 100644 --- a/lib/bait/gen/js/stmt.bt +++ b/lib/bait/gen/js/stmt.bt @@ -124,6 +124,9 @@ fun (mut g Gen) assert_side_expr(node ast.Expr) { } else if node is ast.SelectorExpr { g.assert_side_expr(node.expr) g.write('.${node.field_name}') + } else if node is ast.ComptimeVar { + g.write('$') + g.write(node.name) } else { g.write('UNHANDLED EXPR') } diff --git a/lib/bait/parser/expr.bt b/lib/bait/parser/expr.bt index 231d1f8f..17630b8e 100644 --- a/lib/bait/parser/expr.bt +++ b/lib/bait/parser/expr.bt @@ -32,7 +32,7 @@ fun (mut p Parser) single_expr() ast.Expr { return p.char_literal() } .dollar { - return p.comp_time_var() + return p.comptime_var() } .dot { return p.enum_val(false) @@ -191,11 +191,11 @@ fun (mut p Parser) char_literal()ast.CharLiteral{ } } -fun (mut p Parser) comp_time_var() ast.CompTimeVar{ +fun (mut p Parser) comptime_var() ast.ComptimeVar{ pos := p.pos() p.next() name := p.check_name() - return ast.CompTimeVar{ + return ast.ComptimeVar{ name = name pos = pos } diff --git a/lib/bait/preference/preference.bt b/lib/bait/preference/preference.bt index be28ee1d..6841bff2 100644 --- a/lib/bait/preference/preference.bt +++ b/lib/bait/preference/preference.bt @@ -35,11 +35,6 @@ pub mut: // Error related hide_warnings bool warn_is_error bool - - // Values for compile time pseudo variables - baitexe string - baitdir string - baithash string } pub fun parse_args(args []string) Prefs { @@ -152,12 +147,6 @@ pub fun parse_args(args []string) Prefs { return p } -pub fun (mut p Prefs) set_comptime_vars() { - p.baitexe = os.executable().replace('\\', '\\\\') - p.baitdir = os.dir(p.baitexe).trim_right('\\') - p.baithash = os.exec('git -C ${p.baitdir} rev-parse --short HEAD').stdout.trim_space() -} - fun backend_from_string(s string) Backend { return match s { 'js' { Backend.js } diff --git a/tests/other/comptime_vars_test.bt b/tests/other/comptime_vars_test.bt new file mode 100644 index 00000000..16d63b46 --- /dev/null +++ b/tests/other/comptime_vars_test.bt @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023-present Lukas Neubert +// SPDX-License-Identifier: MPL-2.0 + +import os + +fun test_comptime_vars() { + assert $PKG == 'main' + assert $FILE == 'tests/other/comptime_vars_test.bt'.replace('/', os.PATH_SEP) + assert $LINE == '9' + assert $FILE_LINE == 'tests/other/comptime_vars_test.bt:10'.replace('/', os.PATH_SEP) + assert $FUN == 'test_comptime_vars' + + // The following vars are hard to test + + assert $ABS_FILE.length > $FILE.length + + assert $BAITEXE.length > 0 + + assert $BAITDIR.length < $BAITEXE.length + + assert $BAITHASH.length == 7 +} diff --git a/tests/other/number_test.bt b/tests/other/number_test.bt index fd3be01e..9b843f53 100644 --- a/tests/other/number_test.bt +++ b/tests/other/number_test.bt @@ -1,5 +1,5 @@ -// Copyright (c) 2023-present Lukas Neubert. -// This Source Codeubject to the terms of the Mozilla Public License 2.0. +// SPDX-FileCopyrightText: 2023-present Lukas Neubert +// SPDX-License-Identifier: MPL-2.0 fun test_floats() { f := 1.23 diff --git a/tests/other/ptr_deref_test.bt b/tests/other/ptr_deref_test.bt index d1892560..163a9c57 100644 --- a/tests/other/ptr_deref_test.bt +++ b/tests/other/ptr_deref_test.bt @@ -1,5 +1,5 @@ -// Copyright (c) 2023-present Lukas Neubert. -// This Source Codeubject to the terms of the Mozilla Public License 2.0. +// SPDX-FileCopyrightText: 2023-present Lukas Neubert +// SPDX-License-Identifier: MPL-2.0 fun test_pointer_deref() { n := 42 diff --git a/tests/other/u8_test.bt b/tests/other/u8_test.bt index 03efe0aa..9b9f4ed5 100644 --- a/tests/other/u8_test.bt +++ b/tests/other/u8_test.bt @@ -1,5 +1,5 @@ -// Copyright (c) 2023-present Lukas Neubert. -// This Source Codeubject to the terms of the Mozilla Public License 2.0. +// SPDX-FileCopyrightText: 2023-present Lukas Neubert +// SPDX-License-Identifier: MPL-2.0 fun test_u8_operations() { a := `a`