Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 26 additions & 20 deletions core/Debug.carp
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,37 @@ immediately, raising a `SIGABRT` if it fails.")
(IO.println &(fmt "Invalid memory balance: %d" (Debug.memory-balance)))
(ignore (System.exit 1)))))))

(doc trace "prints the value of an expression to `stdout`, then returns its value.")
(doc trace "prints the expression and its value to `stdout`, then returns its value.")
(defmacro trace [x]
(let [sym (gensym)]
(let [sym (gensym)
x-str (str x)]
`(let-do [%sym %x]
; we use eval here to ensure we resolve the symbol before putting it
; into file, line, and column
(IO.println
(ref
(fmt "%s:%d:%d: %s"
%(eval `(file %x))
%(eval `(line %x))
%(eval `(column %x))
&(str %sym))))
%sym)
)
)
(IO.println
(ref
(fmt "%s:%d:%d: %s = %s"
%(eval `(file %x))
%(eval `(line %x))
%(eval `(column %x))
%x-str
&(prn &%sym))))
%sym)))

(doc leak-array "leaks some memory. This function is useful for testing tools that detect leaks.")
(register leak-array (Fn [a] ()) "Debug_leak_MINUS_array")

)

;; Crash the program with an error message unless the expression evaluates to 'true'.
(defmacro assert [expr]
`(unless (= true %expr)
(do
(println* (fmt "Assertion '%s' failed at line %d, column %d in file %s" %(str expr) %(line) %(column) %(file)))
(System.abort))))
(doc assert "Crash the program with an error message unless the expression evaluates to 'true'. Supports an optional custom message.")
(defmacro assert [expr :rest msg]
(let [has-msg (> (length msg) 0)]
(if has-msg
(let [custom-msg (car msg)]
`(unless (= true %expr)
(let [msg-val (str %custom-msg)]
(do
(println* (fmt "Assertion '%s' failed: %s at line %d, column %d in file %s" %(str expr) &msg-val %(eval `(line %expr)) %(eval `(column %expr)) %(eval `(file %expr))))
(System.abort)))))
`(unless (= true %expr)
(do
(println* (fmt "Assertion '%s' failed at line %d, column %d in file %s" %(str expr) %(eval `(line %expr)) %(eval `(column %expr)) %(eval `(file %expr))))
(System.abort))))))
16 changes: 0 additions & 16 deletions core/Dynamic.carp
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,6 @@ integers [`imod`](#imod).")
(defndynamic tail [s]
(String.suffix s 1))
)

(defmodule Debug
(doc trace "prints the value of an expression to `stdout`, then returns its value.")
(defmacro trace [x]
(let [sym (gensym)]
`(let-do [%sym %x]
; we use eval here to ensure we resolve the symbol before putting it
; into file, line, and column
(macro-log
%(eval `(file %x)) ":"
%(eval `(line %x)) ":"
%(eval `(column %x)) ": "
%sym)
%sym))
)
)
)


Expand Down
11 changes: 11 additions & 0 deletions test/trace_assert_check.carp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
;; This file verifies the improved 'trace' and 'assert' macros in core/Debug.carp

(defn main []
(let [x (Debug.trace (+ 10 20))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the output from these trace calls checked somewhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, you're probably right that it should be though. I didn't think of that!!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be doable if you add it to the tests that check their output.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @eriksvedang! I looked into adding the output check to the produces-output/ suite, but I ran into a bit of a snag with how trace resolves paths.

Because the macro uses (file) under the hood, it evaluates to the absolute path of the file at compile time. If I add it to produces-output, the .expected file would need my hardcoded local path (e.g., /home/.../Carp/test/...) and
would instantly fail on GitHub Actions when the CI runner checks it out to a different directory.

I could write a custom shell script to sanitize $PWD from the test output, but since the rest of the test suite cleanly uses execute.sh, I figured it's better to avoid polluting the test harnesses with one-off hacks. Because of this,
the test currently just executes the macro to ensure it compiles and evaluates properly without crashing. Let me know if you'd still prefer me to add a path-sanitizing hack to the test scripts though!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Does setting file-path-print-length to short work in this context?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is exactly the correct way to have solved this. I didn't think of that at all. Nice catch

y (Debug.trace @"hello")]
(do
(assert (= x 30))
(assert (= x 30) "x must be 30")
;; Print success message
(IO.println "All test assertions passed successfully!")
())))
Loading