diff --git a/builtin-programs/calibrate/calibrate-page.folk b/builtin-programs/calibrate/calibrate-page.folk index d882e0b7..4fb0cb37 100644 --- a/builtin-programs/calibrate/calibrate-page.folk +++ b/builtin-programs/calibrate/calibrate-page.folk @@ -5,16 +5,11 @@ fn codeToPostScript Wish the web server handles route "/calibrate" with hidden true handler { package require base64 - set defaultGeom [dict get [lindex [Query! /someone/ claims the default program geometry is /defaultGeom/] 0] defaultGeom] - fn defaultGeomGet {key} { return [string map {mm ""} [dict get $defaultGeom $key]] } - set camera $QUERY(camera) set display $QUERY(display) # Query the camera resolution for proper aspect ratio in preview (defaults to 1920x1080) - set cameraOpts [QueryOne! camera $camera has width /width/ height /height/] - set cameraWidth [dict get $cameraOpts width] - set cameraHeight [dict get $cameraOpts height] + Expect! camera $camera has width /cameraWidth/ height /cameraHeight/ upvar ^html ^html html [csubst { @@ -120,7 +115,7 @@ Wish the web server handles route "/calibrate" with hidden true handler {
- [dict get [QueryOne! the web navigation HTML is /nav/] nav] + [QueryOne! the web navigation HTML is /./] [HtmlWhen the collected results for [list /programName/ has program code /programCode/] are /programs/ { emitHtmlForPrograms $programs diff --git a/builtin-programs/web/new.folk b/builtin-programs/web/new.folk index 2294956b..c8309d97 100644 --- a/builtin-programs/web/new.folk +++ b/builtin-programs/web/new.folk @@ -170,11 +170,8 @@ Wish the web server handles route "/new" with nav "" namespace import ::math::linearalgebra::matmul namespace import ::math::linearalgebra::scale - set quadLib [dict get [lindex [Query! the quad library is /quadLib/] 0] quadLib] - - set dims [lindex [Query! display /disp/ has width /displayWidth/ height /displayHeight/] 0] - set disp [dict get $dims disp] - set displayWidth [dict get $dims displayWidth]; set displayHeight [dict get $dims displayHeight] + Expect! the quad library is /quadLib/ + Expect! display /disp/ has width /displayWidth/ height /displayHeight/ set x [expr {int(double(${(left + (left/window.innerWidth) * w)}) * (double($displayWidth) / ${window.innerWidth}))}] set y [expr {int(double(${(top + (top/window.innerHeight) * h)}) * (double($displayHeight) / ${window.innerHeight}))}] @@ -183,7 +180,7 @@ Wish the web server handles route "/new" with nav "" # Create 3D vertices in meters that will project to the desired pixel coordinates # Using a depth of 1.5 meters from the projector center set depth 1.5 - set displayIntrinsics [dict get [lindex [Query! display $disp has intrinsics /displayIntrinsics/] 0] displayIntrinsics] + Expect! display $disp has intrinsics /displayIntrinsics/ # Convert pixel coordinates to 3D coordinates in projector space set fx [dict get $displayIntrinsics fx] diff --git a/builtin-programs/web/page.folk b/builtin-programs/web/page.folk index 506e45bf..7bf186dc 100644 --- a/builtin-programs/web/page.folk +++ b/builtin-programs/web/page.folk @@ -1,5 +1,5 @@ Wish the web server handles route {/page/(.*)$} with handler { - set programDir [dict get [QueryOne! the program save directory is /saveDir/] saveDir] + Expect! the program save directory is /programDir/ set program_id $1 set filenames [list \ "$programDir/$program_id.folk" \ diff --git a/builtin-programs/web/printed-programs.folk b/builtin-programs/web/printed-programs.folk index e66550bf..cc661a1e 100644 --- a/builtin-programs/web/printed-programs.folk +++ b/builtin-programs/web/printed-programs.folk @@ -1,5 +1,5 @@ Wish the web server handles route {/printed-programs/([^/]+)\.folk$} with handler { - set programDir [dict get [QueryOne! the program save directory is /programDir/] programDir] + Expect! the program save directory is /programDir/ set filename "$programDir/$1.folk" set fp [open $filename r] set data [read $fp] diff --git a/builtin-programs/web/program.folk b/builtin-programs/web/program.folk index 11780baa..84d0de0f 100644 --- a/builtin-programs/web/program.folk +++ b/builtin-programs/web/program.folk @@ -1,6 +1,6 @@ Wish the web server handles route {/program/(.*)$} with handler { set programName $1 - set match [QueryOne! $programName has program code /programCode/] + Expect! $programName has program code /programCode/] if {$match eq ""} { html "Program not found: [htmlEscape $programName]" return diff --git a/lib/folk.js b/lib/folk.js index 07f220de..6ea6dbb8 100644 --- a/lib/folk.js +++ b/lib/folk.js @@ -337,7 +337,7 @@ ${message} with environment [list [list this $chan __ctx $ctx __seq ${seqNumber} console.log('watchCollected', statement); const retractKey = await this.send(tcl`Say when the collected results for ${statement} are /result/ { - if {![info exists ::wsLib]} { set ::wsLib [dict get [lindex [Query! /someone/ claims the websocket library is /wsLib/] 0] wsLib] } + if {![info exists ::wsLib]} { set ::wsLib [QueryOne! the websocket library is /./] } $::wsLib wsEmitMsg $__ctx [list ${channel.prefix} $result] } with environment [list [list this $this __ctx $__ctx]]`); channel.retractKey = retractKey; @@ -352,7 +352,7 @@ ${message} with environment [list [list this $chan __ctx $ctx __seq ${seqNumber} }); const retractKey = await this.send(tcl` - if {![info exists ::wsLib]} { set ::wsLib [dict get [lindex [Query! /someone/ claims the websocket library is /wsLib/] 0] wsLib] } + if {![info exists ::wsLib]} { set ::wsLib [QueryOne! the websocket library is /./] } set statement ${statement} # enumerate variable names this statement uses @@ -393,7 +393,7 @@ ${message} with environment [list [list this $chan __ctx $ctx __seq ${seqNumber} }); const retractKey = await this.send(tcl` - if {![info exists ::wsLib]} { set ::wsLib [dict get [lindex [Query! /someone/ claims the websocket library is /wsLib/] 0] wsLib] } + if {![info exists ::wsLib]} { set ::wsLib [QueryOne! the websocket library is /./] } set pattern ${statement} set body { diff --git a/prelude.tcl b/prelude.tcl index f208fb0c..30276580 100644 --- a/prelude.tcl +++ b/prelude.tcl @@ -612,6 +612,9 @@ proc On {event args} { } } +# Synchronous, sampling query of the db. Returns a list of dicts where +# each dict is a statement matching the pattern. +# # Query! is like QuerySimple! but with added support for & joins, and # it'll automatically also query the claimized pattern (the pattern # with `/someone/ claims` prepended). @@ -700,15 +703,55 @@ proc Query! {args} { } return $results } + +# Synchronous, sampling query of the db. Throws unless there is +# exactly 1 statement result. Returns a dict whose keys are bound keys +# from the pattern and whose values are corresponding terms from the +# statement. +# +# Use like this: +# +# Claim the dog is cool +# sleep 0.5 +# puts [dict get [QueryOne! the dog is /val/] val] ;# -> cool +# proc QueryOne! {args} { - set results [Query! {*}$args] + set pattern [list] + for {set i 0} {$i < [llength $args]} {incr i} { + set arg [lindex $args $i] + if {$arg eq "-default"} { + incr i + set default [lindex $args $i] + } else { + lappend pattern $arg + } + } + set results [Query! {*}$args] + if {[llength $results] == 0 && [info exists default]} { + return $default + } if {[llength $results] != 1} { error "QueryOne! of ($args) had [llength $results] results. Should be one result!" } + if {{/./} in $args} { + return [dict get [lindex $results 0] .] + } return [lindex $results 0] } + +# Like QueryOne!, but introduces the bindings directly into caller +# scope. +# +# Expect! the dog is /val/ +# puts $val ;# -> cool +# +proc Expect! {args} { + dict for {k v} [QueryOne! {*}$args] { + uplevel [list set $k $v] + } +} proc ForEach! {args} { set body [lindex $args end] set pattern [lreplace $args end end]