Fix codegen, implement 8bitsy dialog editor

This commit is contained in:
Jeremy Penner 2021-04-24 23:39:50 -04:00
parent a8d77b232c
commit fdf69b8b11
14 changed files with 139 additions and 106 deletions

Binary file not shown.

View file

@ -179,7 +179,7 @@
(fn process-pdat [pdat process default ...] (fn process-pdat [pdat process default ...]
(fn complain [ok ...] (fn complain [ok ...]
(if ok (values ...) (if ok (values ...)
(do (pp pdat) (error (.. process " failed in " pdat.type " near " (or pdat.nearest-symbol "<start of block>") " @" (or pdat.addr "<no address>") " - " ...))))) (do (error (.. process " failed in " pdat.type " near " (or pdat.nearest-symbol "<start of block>") " @" (or pdat.addr "<no address>") " - " ...)))))
(local processor (. pdat-processor pdat.type process)) (local processor (. pdat-processor pdat.type process))
(if processor (complain (pcall #(processor pdat $...) ...)) default)) (if processor (complain (pcall #(processor pdat $...) ...)) default))
@ -256,7 +256,6 @@
(local block-env (make-env block env)) (local block-env (make-env block env))
(var bytes "") (var bytes "")
(each [_ pdat (ipairs block.pdats)] (each [_ pdat (ipairs block.pdats)]
(print pdat.type pdat.addr pdat.nearest-symbol)
(process-pdat pdat :generate nil block-env) (process-pdat pdat :generate nil block-env)
(local pdatbytes (process-pdat pdat :bytes pdat.bytes block-env)) (local pdatbytes (process-pdat pdat :bytes pdat.bytes block-env))
(assert (= (type pdatbytes) :string) (.. "failed to generate bytes: " (fv pdat))) (assert (= (type pdatbytes) :string) (.. "failed to generate bytes: " (fv pdat)))

29
editor/8bitsy.fnl Normal file
View file

@ -0,0 +1,29 @@
(local util (require :lib.util))
(local actions (require :editor.actions))
(local {: textbox : dropdown } (util.require :editor.imstate))
(local files (require :game.files))
(local lume (require :lib.lume))
(local style (require :core.style))
(actions.register :say
(fn [action view x y w i]
(let [characters (lume.map files.game.portraits #$1.label)
character (or action.character (. characters 1))
lines (or action.lines [])
(character y) (dropdown view [:say :char i] character characters x (+ y style.padding.y) 300)
(line1 y) (textbox view [:say :line1 i] (. lines 1) x (+ y style.padding.y) 300)
(line2 y) (textbox view [:say :line2 i] (. lines 2) x y 300)
(line3 y) (textbox view [:say :line3 i] (. lines 3) x y 300)
(line4 y) (textbox view [:say :line4 i] (. lines 4) x y 300)
]
(set action.character character)
(util.nested-tset action [:lines 1] line1)
(util.nested-tset action [:lines 2] line2)
(util.nested-tset action [:lines 3] line3)
(util.nested-tset action [:lines 4] line4)
y))
(fn [action vm]
(local {: say} (require :game.defs))
(say action.character (table.unpack (lume.map action.lines #($1:upper))))))
{}

View file

@ -9,4 +9,11 @@
(defmethod actions.edit :default (fn [action view x y w i] y)) (defmethod actions.edit :default (fn [action view x y w i] y))
(fn actions.register [key edit generate]
(when (= actions.actionlist nil)
(set actions.actionlist []))
(table.insert actions.actionlist key)
(defmethod actions.edit key edit)
(defmethod actions.generate key generate))
actions.hot actions.hot

View file

@ -9,6 +9,8 @@
(local keymap (require :core.keymap)) (local keymap (require :core.keymap))
(local common (require :core.common)) (local common (require :core.common))
(require :editor.8bitsy)
(let [commands {}] (let [commands {}]
(each [_ name (ipairs [:tile :portrait :font :brush :map])] (each [_ name (ipairs [:tile :portrait :font :brush :map])]
(local cls (require (.. "editor." name "edit"))) (local cls (require (.. "editor." name "edit")))

View file

@ -104,6 +104,7 @@
(fn MapEditView.draw-map-editor [self x y] (fn MapEditView.draw-map-editor [self x y]
(love.graphics.setColor 1 1 1 1) (love.graphics.setColor 1 1 1 1)
(local button-state self.imstate.left)
(activate self :map x y (* tilew mapw) (* tileh maph)) (activate self :map x y (* tilew mapw) (* tileh maph))
(var iobject-over nil) (var iobject-over nil)
(for [my 1 maph] (for [my 1 maph]
@ -130,7 +131,7 @@
(when (and self.itile (active? self :map) (mouse-inside tilex tiley tilew tileh) (not= itile self.itile)) (when (and self.itile (active? self :map) (mouse-inside tilex tiley tilew tileh) (not= itile self.itile))
(self:set-tile mx my self.itile)) (self:set-tile mx my self.itile))
(when (and (= self.itile nil) (active? self :map) (mouse-inside tilex tiley tilew tileh)) (when (and (= self.itile nil) (active? self :map) (mouse-inside tilex tiley tilew tileh))
(match self.imstate.left (match button-state
:pressed (set self.iobject-linking iobject) :pressed (set self.iobject-linking iobject)
:released :released
(if (and (not= iobject nil) (= self.iobject-linking iobject)) (if (and (not= iobject nil) (= self.iobject-linking iobject))

View file

@ -1,9 +1,4 @@
; TODO: Generate from data? ; TODO: Generate from data?
(local {: vm : say : deflevel} (require :game.defs)) (local {: vm : say : deflevel} (require :game.defs))
(deflevel "game/map1.json" :map1) (deflevel 1 :map1)
(vm:word :cat (say :cat "I'M A CAT"))
(vm:word :fish (say :angryfish "I'M AN ANGRY FISH" "GRRRR"))
(vm:word :pot (say :player "IT'S A POT.") (say :player "NOTHING UNUSUAL ABOUT IT.") (say :player "JUST AN ORDINARY POT."))
(vm:word :suspiciouspot (say :player "IT'S A POT.") (say :player "NOTHING UNUSUAL ABOUT IT.") (say :player "JUST AN ORDINARY POT.") (say :player "YEP, NOTHING SPECIAL HERE."))

View file

@ -4,7 +4,9 @@
(local asm (require :asm.asm)) (local asm (require :asm.asm))
(local VM (require :asm.vm)) (local VM (require :asm.vm))
(local tiles (require :game.tiles)) (local tiles (require :game.tiles))
(local files (require :game.files))
(local Prodos (require :asm.prodos)) (local Prodos (require :asm.prodos))
(local actions (require :editor.actions))
(local prg (asm.new)) (local prg (asm.new))
(local vm (VM.new prg {:org 0xc00})) (local vm (VM.new prg {:org 0xc00}))
@ -123,7 +125,7 @@
(fn append-map [map org label] (fn append-map [map org label]
(org:append (org:append
[:align 0x100] label [:align 0x100] label
[:bytes (map.map:fromhex)] [:bytes map.map]
[:db (length map.objects)] [:db (length map.objects)]
[:dw (tiles.encode-yx map.player)] [:dw (tiles.encode-yx map.player)]
[:jmp (if (= (or map.tickword "") "") :next map.tickword)] [:jmp (if (= (or map.tickword "") "") :next map.tickword)]
@ -138,18 +140,27 @@
(vm:word :map-specific-move :map 246 :+ :execute) (vm:word :map-specific-move :map 246 :+ :execute)
(vm:word :map-specific-load :map 249 :+ :execute) (vm:word :map-specific-load :map 249 :+ :execute)
(fn deflevel [mapfile label] (fn generate-entity-code [level vm prefix]
(each [ientity entity (ipairs level.objects)]
(when (not entity.advanced)
(let [code []]
(each [iaction action (ipairs (or entity.steps []))]
(lume.push code (actions.generate action vm iaction)))
(vm:word (.. prefix ientity) (table.unpack code))))))
(fn deflevel [ilevel label]
(local level prg) ; todo: (asm.new prg) - if we want to load levels as an overlay (local level prg) ; todo: (asm.new prg) - if we want to load levels as an overlay
(local org level.vm.code) ; (level:org org.level.org) - if we want to give level data a stable loxation (local org level.vm.code) ; (level:org org.level.org) - if we want to give level data a stable loxation
(local map (readjson mapfile)) (local map (. files.game.levels ilevel))
(local entity (require :game.entity)) (local entity (require :game.entity))
(append-map map org label) (append-map map org label)
(entity.append-from-map map org label) (entity.append-from-map map org label)
(set level.vm.code org) (set level.vm.code org)
(generate-entity-code map level.vm (.. label "-entity-word-"))
level) level)
(fn say-runon [portrait ...] (fn say-runon [portrait ...]
(local result [:vm (.. :draw-p portrait)]) (local result [:vm (.. :draw-portrait- portrait)])
(local lines [...]) (local lines [...])
(local ilineOffset (if (< (length lines) 4) 1 0)) (local ilineOffset (if (< (length lines) 4) 1 0))
(each [iline line (ipairs lines)] (each [iline line (ipairs lines)]
@ -161,10 +172,9 @@
(table.insert result :dismiss-dialog) (table.insert result :dismiss-dialog)
result) result)
(local tilelist (tiles.loadgfx tiles.fn-tiles)) (fn itile [label] (tiles.find-itile files.game.tiles label))
(fn itile [label] (tiles.find-itile tilelist label))
(set vm.code org.code) (set vm.code org.code)
{: vm : prg : mapw : maph : mon : org : achar : astr : style : rot8l : deflevel : say : say-runon : itile : tilelist} {: vm : prg : mapw : maph : mon : org : achar : astr : style : rot8l : deflevel : say : say-runon : itile}

View file

@ -100,10 +100,10 @@
(entity-org:append (entity-org:append
(.. prefix "-entity-" ientity) (.. prefix "-entity-" ientity)
[:db (- entity.x 1)] [:db (- entity.y 1)] [:db (- entity.x 1)] [:db (- entity.y 1)]
[:ref entity.func] [:ref (if entity.advanced entity.func (.. prefix "-entity-word-" ientity))]
(if (and entity.linkword (> (length entity.linkword) 0)) [:ref entity.linkword] [:dw 0]) (if (and entity.advanced entity.linkword (> (length entity.linkword) 0)) [:ref entity.linkword] [:dw 0])
(if entity.link [:ref (.. prefix "-entity-" entity.link)] (if entity.link [:ref (.. prefix "-entity-" entity.link)]
entity.linkentity [:ref entity.linkentity] (and entity.advanced entity.linkentity) [:ref entity.linkentity]
[:dw 0])))) [:dw 0]))))
{: ev : append-from-map} {: ev : append-from-map}

View file

@ -1,21 +1,39 @@
(local util (require :lib.util)) (local util (require :lib.util))
(local lume (require :lib.lume)) (local lume (require :lib.lume))
(local tile (require :game.tiles))
(local tiledraw (require :editor.tiledraw)) (local tiledraw (require :editor.tiledraw))
(local files (util.hot-table ...)) (local files (util.hot-table ...))
(local filename "game/game.json") (local filename "game/game.json")
(local encoded-tile-fields [:gfx :mask])
(fn convert [tile field method]
(local oldval (. tile field))
(when oldval
(tset tile field (: oldval method)))
tile)
(fn convert-all [tile method]
(each [_ field (ipairs encoded-tile-fields)]
(convert tile field method))
tile)
(fn tile-deserialize [tile]
(match (type tile)
:string {:gfx (tile:fromhex) :flags {}}
:table (convert-all tile :fromhex)))
(fn tile-serialize [tile] (convert-all (lume.clone tile) :tohex))
(fn deserialize [key value] (fn deserialize [key value]
(match key (match key
(where (or :tiles :portraits :font)) (tile.deserialize value) (where (or :tiles :portraits :font)) (tile-deserialize value)
:levels (do (set value.map (value.map:fromhex)) value) :levels (do (set value.map (value.map:fromhex)) value)
_ value)) _ value))
(fn serialize [key value] (fn serialize [key value]
(match key (match key
(where (or :tiles :portraits :font)) (tile.serialize value) (where (or :tiles :portraits :font)) (tile-serialize value)
:levels (do (set value.map (value.map:tohex)) value) :levels (do (set value.map (value.map:tohex)) value)
_ value)) _ value))

File diff suppressed because one or more lines are too long

View file

@ -2,6 +2,7 @@
(local {: lo : hi : readjson} util) (local {: lo : hi : readjson} util)
(local tile (util.reload :game.tiles)) (local tile (util.reload :game.tiles))
(local {: prg : vm : org} (util.reload :game.defs)) (local {: prg : vm : org} (util.reload :game.defs))
(local files (require :game.files))
(local disk (util.reload :game.disk)) (local disk (util.reload :game.disk))
@ -14,7 +15,7 @@
(tile.appendtiles org.code) (tile.appendtiles org.code)
(org.code:append [:align 0x100] :font) (org.code:append [:align 0x100] :font)
(tile.appendgfx org.code (tile.loadgfx tile.fn-font)) (tile.appendgfx org.code files.game.font)
(tile.append-portraitwords vm) (tile.append-portraitwords vm)
(vm:var :tick-count) (vm:var :tick-count)

View file

@ -1,50 +1,21 @@
(local util (require :lib.util)) (local util (require :lib.util))
(local lume (require :lib.lume)) (local lume (require :lib.lume))
(local files (require :game.files))
(local flags [:walkable]) (local flags [:walkable])
(local flag-to-bit {}) (local flag-to-bit {})
(each [iflag flag (ipairs flags)] (each [iflag flag (ipairs flags)]
(tset flag-to-bit flag (bit.lshift 1 (- iflag 1)))) (tset flag-to-bit flag (bit.lshift 1 (- iflag 1))))
(local encoded-tile-fields [:gfx :mask]) (fn appendgfx [org gfx ?key ?label-prefix]
(fn convert [tile field method]
(local oldval (. tile field))
(when oldval
(tset tile field (: oldval method)))
tile)
(fn convert-all [tile method]
(each [_ field (ipairs encoded-tile-fields)]
(convert tile field method))
tile)
(fn deserialize [tile]
(match (type tile)
:string {:gfx (tile:fromhex) :flags {}}
:table (convert-all tile :fromhex)))
(fn serialize [tile] (convert-all (lume.clone tile) :tohex))
(local fn-tiles "game/tiles.json")
(local fn-portraits "game/portraits.json")
(local fn-font "game/font.json")
(fn loadgfx [filename]
(if (util.file-exists filename)
(lume.map (util.readjson filename) deserialize)
[]))
(fn savegfx [filename gfx] (util.writejson filename (lume.map gfx serialize)))
(fn appendgfx [org gfx ?key ?ignore-labels]
(each [_ g (ipairs gfx)] (each [_ g (ipairs gfx)]
(when (and g.label (not ?ignore-labels)) (org:append g.label)) (when g.label (org:append (.. (or ?label-prefix "") g.label)))
(org:append [:bytes (. g (or ?key :gfx))]))) (org:append [:bytes (. g (or ?key :gfx))])))
(fn appendtiles [org] (fn appendtiles [org]
(local tiles (loadgfx fn-tiles)) (local tiles files.game.tiles)
(org:append [:align 0x100] :tileset) (org:append [:align 0x100] :tileset)
(appendgfx org tiles) (appendgfx org tiles)
(appendgfx org (loadgfx fn-portraits)) (appendgfx org files.game.portraits nil :portrait-)
(org:append :tileflags) (org:append :tileflags)
(each [_ tile (ipairs tiles)] (each [_ tile (ipairs tiles)]
(var flags 0) (var flags 0)
@ -54,11 +25,11 @@
(fn append-portraitwords [vm ?overrides] (fn append-portraitwords [vm ?overrides]
(local overrides (or ?overrides {})) (local overrides (or ?overrides {}))
(each [_ p (ipairs (loadgfx fn-portraits))] (each [_ p (ipairs files.game.portraits)]
(let [wordname (.. :draw- p.label) (let [wordname (.. :draw-portrait- p.label)
override (. overrides p.label)] override (. overrides p.label)]
(vm:word (.. :draw- p.label) :show-footer (vm:word wordname :show-footer
(if override (override p.label) [:vm :lit p.label]) (if override (override p.label) [:vm :lit (.. :portrait- p.label)])
:draw-portrait)))) :draw-portrait))))
(fn encode-yx [xy] (fn encode-yx [xy]
@ -81,6 +52,6 @@
(if (= tile.label label) (encode-itile itile) (if (= tile.label label) (encode-itile itile)
(find-itile tiles label (+ itile 1)))) (find-itile tiles label (+ itile 1))))
{: loadgfx : savegfx : appendtiles : appendgfx : append-portraitwords : flags : flag-to-bit : find-itile {: appendtiles : appendgfx : append-portraitwords : flags : flag-to-bit : find-itile
: fn-tiles : fn-portraits : fn-font : encode-yx : encode-itile : decode-itile : serialize : deserialize} : encode-yx : encode-itile : decode-itile}

View file

@ -2223,8 +2223,8 @@ package.preload["fennel.compiler"] = package.preload["fennel.compiler"] or funct
return table.concat(utils.map(exprs, 1), ", ") return table.concat(utils.map(exprs, 1), ", ")
end end
local function disambiguate_parens(code, chunk) local function disambiguate_parens(code, chunk)
if ((code:byte() == 40) and (1 < #chunk)) then if (code:byte() == 40) then
return ("; " .. code) return ("do end " .. code)
else else
return code return code
end end
@ -3712,13 +3712,13 @@ do
local builtin_macros = [===[;; This module contains all the built-in Fennel macros. Unlike all the other local builtin_macros = [===[;; This module contains all the built-in Fennel macros. Unlike all the other
;; modules that are loaded by the old bootstrap compiler, this runs in the ;; modules that are loaded by the old bootstrap compiler, this runs in the
;; compiler scope of the version of the compiler being defined. ;; compiler scope of the version of the compiler being defined.
;; The code for these macros is somewhat idiosyncratic because it cannot use any ;; The code for these macros is somewhat idiosyncratic because it cannot use any
;; macros which have not yet been defined. ;; macros which have not yet been defined.
;; TODO: some of these macros modify their arguments; we should stop doing that, ;; TODO: some of these macros modify their arguments; we should stop doing that,
;; but in a way that preserves file/line metadata. ;; but in a way that preserves file/line metadata.
(fn ->* [val ...] (fn ->* [val ...]
"Thread-first macro. "Thread-first macro.
Take the first value and splice it into the second form as its first argument. Take the first value and splice it into the second form as its first argument.
@ -3729,7 +3729,7 @@ do
(table.insert elt 2 x) (table.insert elt 2 x)
(set x elt))) (set x elt)))
x) x)
(fn ->>* [val ...] (fn ->>* [val ...]
"Thread-last macro. "Thread-last macro.
Same as ->, except splices the value into the last position of each form Same as ->, except splices the value into the last position of each form
@ -3740,7 +3740,7 @@ do
(table.insert elt x) (table.insert elt x)
(set x elt))) (set x elt)))
x) x)
(fn -?>* [val ...] (fn -?>* [val ...]
"Nil-safe thread-first macro. "Nil-safe thread-first macro.
Same as -> except will short-circuit with nil when it encounters a nil value." Same as -> except will short-circuit with nil when it encounters a nil value."
@ -3755,7 +3755,7 @@ do
(if ,tmp (if ,tmp
(-?> ,el ,(unpack els)) (-?> ,el ,(unpack els))
,tmp))))) ,tmp)))))
(fn -?>>* [val ...] (fn -?>>* [val ...]
"Nil-safe thread-last macro. "Nil-safe thread-last macro.
Same as ->> except will short-circuit with nil when it encounters a nil value." Same as ->> except will short-circuit with nil when it encounters a nil value."
@ -3770,14 +3770,14 @@ do
(if ,tmp (if ,tmp
(-?>> ,el ,(unpack els)) (-?>> ,el ,(unpack els))
,tmp))))) ,tmp)))))
(fn ?dot [tbl k ...] (fn ?dot [tbl k ...]
"Nil-safe table look up. "Nil-safe table look up.
Same as . (dot), except will short-circuit with nil when it encounters Same as . (dot), except will short-circuit with nil when it encounters
a nil value in any of subsequent keys." a nil value in any of subsequent keys."
(if (= nil k) tbl `(let [res# (. ,tbl ,k)] (if (= nil k) tbl `(let [res# (. ,tbl ,k)]
(and res# (?. res# ,...))))) (and res# (?. res# ,...)))))
(fn doto* [val ...] (fn doto* [val ...]
"Evaluates val and splices it into the first argument of subsequent forms." "Evaluates val and splices it into the first argument of subsequent forms."
(let [name (gensym) (let [name (gensym)
@ -3787,7 +3787,7 @@ do
(table.insert form elt)) (table.insert form elt))
(table.insert form name) (table.insert form name)
form)) form))
(fn when* [condition body1 ...] (fn when* [condition body1 ...]
"Evaluate body for side-effects only when condition is truthy." "Evaluate body for side-effects only when condition is truthy."
(assert body1 "expected body") (assert body1 "expected body")
@ -3795,7 +3795,7 @@ do
(do (do
,body1 ,body1
,...))) ,...)))
(fn with-open* [closable-bindings ...] (fn with-open* [closable-bindings ...]
"Like `let`, but invokes (v:close) on each binding after evaluating the body. "Like `let`, but invokes (v:close) on each binding after evaluating the body.
The body is evaluated inside `xpcall` so that bound values will be closed upon The body is evaluated inside `xpcall` so that bound values will be closed upon
@ -3812,13 +3812,13 @@ do
`(let ,closable-bindings `(let ,closable-bindings
,closer ,closer
(close-handlers# (xpcall ,bodyfn ,traceback))))) (close-handlers# (xpcall ,bodyfn ,traceback)))))
(fn collect* [iter-tbl key-value-expr ...] (fn collect* [iter-tbl key-value-expr ...]
"Returns a table made by running an iterator and evaluating an expression "Returns a table made by running an iterator and evaluating an expression
that returns key-value pairs to be inserted sequentially into the table. that returns key-value pairs to be inserted sequentially into the table.
This can be thought of as a \"table comprehension\". The provided key-value This can be thought of as a \"table comprehension\". The provided key-value
expression must return either 2 values, or nil. expression must return either 2 values, or nil.
For example, For example,
(collect [k v (pairs {:apple \"red\" :orange \"orange\"})] (collect [k v (pairs {:apple \"red\" :orange \"orange\"})]
(values v k)) (values v k))
@ -3834,12 +3834,12 @@ do
(match ,key-value-expr (match ,key-value-expr
(k# v#) (tset tbl# k# v#))) (k# v#) (tset tbl# k# v#)))
tbl#)) tbl#))
(fn icollect* [iter-tbl value-expr ...] (fn icollect* [iter-tbl value-expr ...]
"Returns a sequential table made by running an iterator and evaluating an "Returns a sequential table made by running an iterator and evaluating an
expression that returns values to be inserted sequentially into the table. expression that returns values to be inserted sequentially into the table.
This can be thought of as a \"list comprehension\". This can be thought of as a \"list comprehension\".
For example, For example,
(icollect [_ v (ipairs [1 2 3 4 5])] (when (> v 2) (* v v))) (icollect [_ v (ipairs [1 2 3 4 5])] (when (> v 2) (* v v)))
returns returns
@ -3853,7 +3853,7 @@ do
(each ,iter-tbl (each ,iter-tbl
(tset tbl# (+ (length tbl#) 1) ,value-expr)) (tset tbl# (+ (length tbl#) 1) ,value-expr))
tbl#)) tbl#))
(fn partial* [f ...] (fn partial* [f ...]
"Returns a function with all arguments partially applied to f." "Returns a function with all arguments partially applied to f."
(assert f "expected a function to partially apply") (assert f "expected a function to partially apply")
@ -3861,10 +3861,10 @@ do
(table.insert body _VARARG) (table.insert body _VARARG)
`(fn [,_VARARG] `(fn [,_VARARG]
,body))) ,body)))
(fn pick-args* [n f] (fn pick-args* [n f]
"Creates a function of arity n that applies its arguments to f. "Creates a function of arity n that applies its arguments to f.
For example, For example,
(pick-args 2 func) (pick-args 2 func)
expands to expands to
@ -3876,10 +3876,10 @@ do
(tset bindings i (gensym))) (tset bindings i (gensym)))
`(fn ,bindings `(fn ,bindings
(,f ,(unpack bindings))))) (,f ,(unpack bindings)))))
(fn pick-values* [n ...] (fn pick-values* [n ...]
"Like the `values` special, but emits exactly n values. "Like the `values` special, but emits exactly n values.
For example, For example,
(pick-values 2 ...) (pick-values 2 ...)
expands to expands to
@ -3894,7 +3894,7 @@ do
(if (= n 0) `(values) (if (= n 0) `(values)
`(let [,let-syms ,let-values] `(let [,let-syms ,let-values]
(values ,(unpack let-syms)))))) (values ,(unpack let-syms))))))
(fn lambda* [...] (fn lambda* [...]
"Function literal with arity checking. "Function literal with arity checking.
Will throw an exception if a declared argument is passed in as nil, unless Will throw an exception if a declared argument is passed in as nil, unless
@ -3921,26 +3921,26 @@ do
,(tostring a) ,(tostring a)
,(or a.filename :unknown) ,(or a.filename :unknown)
,(or a.line "?")))))) ,(or a.line "?"))))))
(assert (= :table (type arglist)) "expected arg list") (assert (= :table (type arglist)) "expected arg list")
(each [_ a (ipairs arglist)] (each [_ a (ipairs arglist)]
(check! a)) (check! a))
(if empty-body? (if empty-body?
(table.insert args (sym :nil))) (table.insert args (sym :nil)))
`(fn ,(unpack args)))) `(fn ,(unpack args))))
(fn macro* [name ...] (fn macro* [name ...]
"Define a single macro." "Define a single macro."
(assert (sym? name) "expected symbol for macro name") (assert (sym? name) "expected symbol for macro name")
(local args [...]) (local args [...])
`(macros {,(tostring name) (fn ,(unpack args))})) `(macros {,(tostring name) (fn ,(unpack args))}))
(fn macrodebug* [form return?] (fn macrodebug* [form return?]
"Print the resulting form after performing macroexpansion. "Print the resulting form after performing macroexpansion.
With a second argument, returns expanded form as a string instead of printing." With a second argument, returns expanded form as a string instead of printing."
(let [handle (if return? `do `print)] (let [handle (if return? `do `print)]
`(,handle ,(view (macroexpand form _SCOPE))))) `(,handle ,(view (macroexpand form _SCOPE)))))
(fn import-macros* [binding1 module-name1 ...] (fn import-macros* [binding1 module-name1 ...]
"Binds a table of macros from each macro module according to a binding form. "Binds a table of macros from each macro module according to a binding form.
Each binding form can be either a symbol or a k/v destructuring table. Each binding form can be either a symbol or a k/v destructuring table.
@ -3971,9 +3971,9 @@ do
(tostring modname))) (tostring modname)))
(tset scope.macros import-key (. subscope.macros macro-name)))))) (tset scope.macros import-key (. subscope.macros macro-name))))))
nil) nil)
;;; Pattern matching ;;; Pattern matching
(fn match-values [vals pattern unifications match-pattern] (fn match-values [vals pattern unifications match-pattern]
(let [condition `(and) (let [condition `(and)
bindings []] bindings []]
@ -3984,7 +3984,7 @@ do
(each [_ b (ipairs subbindings)] (each [_ b (ipairs subbindings)]
(table.insert bindings b)))) (table.insert bindings b))))
(values condition bindings))) (values condition bindings)))
(fn match-table [val pattern unifications match-pattern] (fn match-table [val pattern unifications match-pattern]
(let [condition `(and (= (type ,val) :table)) (let [condition `(and (= (type ,val) :table))
bindings []] bindings []]
@ -4016,7 +4016,7 @@ do
(each [_ b (ipairs subbindings)] (each [_ b (ipairs subbindings)]
(table.insert bindings b))))) (table.insert bindings b)))))
(values condition bindings))) (values condition bindings)))
(fn match-pattern [vals pattern unifications] (fn match-pattern [vals pattern unifications]
"Takes the AST of values and a single pattern and returns a condition "Takes the AST of values and a single pattern and returns a condition
to determine if it matches as well as a list of bindings to to determine if it matches as well as a list of bindings to
@ -4056,7 +4056,7 @@ do
(match-table val pattern unifications match-pattern) (match-table val pattern unifications match-pattern)
;; literal value ;; literal value
(values `(= ,val ,pattern) [])))) (values `(= ,val ,pattern) []))))
(fn match-condition [vals clauses] (fn match-condition [vals clauses]
"Construct the actual `if` AST for the given match values and clauses." "Construct the actual `if` AST for the given match values and clauses."
(if (not= 0 (% (length clauses) 2)) ; treat odd final clause as default (if (not= 0 (% (length clauses) 2)) ; treat odd final clause as default
@ -4070,7 +4070,7 @@ do
(table.insert out `(let ,bindings (table.insert out `(let ,bindings
,body)))) ,body))))
out)) out))
(fn match-val-syms [clauses] (fn match-val-syms [clauses]
"How many multi-valued clauses are there? return a list of that many gensyms." "How many multi-valued clauses are there? return a list of that many gensyms."
(let [syms (list (gensym))] (let [syms (list (gensym))]
@ -4080,7 +4080,7 @@ do
(if (not (. syms valnum)) (if (not (. syms valnum))
(tset syms valnum (gensym)))))) (tset syms valnum (gensym))))))
syms)) syms))
(fn match* [val ...] (fn match* [val ...]
;; Old implementation of match macro, which doesn't directly support ;; Old implementation of match macro, which doesn't directly support
;; `where' and `or'. New syntax is implemented in `match-where', ;; `where' and `or'. New syntax is implemented in `match-where',
@ -4090,9 +4090,9 @@ do
;; protect against multiple evaluation of the value, bind against as ;; protect against multiple evaluation of the value, bind against as
;; many values as we ever match against in the clauses. ;; many values as we ever match against in the clauses.
(list `let [vals val] (match-condition vals clauses)))) (list `let [vals val] (match-condition vals clauses))))
;; Construction of old match syntax from new syntax ;; Construction of old match syntax from new syntax
(fn partition-2 [seq] (fn partition-2 [seq]
;; Partition `seq` by 2. ;; Partition `seq` by 2.
;; If `seq` has odd amount of elements, the last one is dropped. ;; If `seq` has odd amount of elements, the last one is dropped.
@ -4112,7 +4112,7 @@ do
(if (not= nil v2) (if (not= nil v2)
(table.insert res [v1 v2])))) (table.insert res [v1 v2]))))
res)) res))
(fn transform-or [[_ & pats] guards] (fn transform-or [[_ & pats] guards]
;; Transforms `(or pat pats*)` lists into match `guard` patterns. ;; Transforms `(or pat pats*)` lists into match `guard` patterns.
;; ;;
@ -4121,7 +4121,7 @@ do
(each [_ pat (ipairs pats)] (each [_ pat (ipairs pats)]
(table.insert res (list pat `? (unpack guards)))) (table.insert res (list pat `? (unpack guards))))
res)) res))
(fn transform-cond [cond] (fn transform-cond [cond]
;; Transforms `where` cond into sequence of `match` guards. ;; Transforms `where` cond into sequence of `match` guards.
;; ;;
@ -4136,12 +4136,12 @@ do
[(list second `? (unpack cond 3))])) [(list second `? (unpack cond 3))]))
:else :else
[cond])) [cond]))
(fn match-where [val ...] (fn match-where [val ...]
"Perform pattern matching on val. See reference for details. "Perform pattern matching on val. See reference for details.
Syntax: Syntax:
(match data-expression (match data-expression
pattern body pattern body
(where pattern guard guards*) body (where pattern guard guards*) body
@ -4157,7 +4157,7 @@ do
(if else-branch (if else-branch
(table.insert match-body else-branch)) (table.insert match-body else-branch))
(match* val (unpack match-body)))) (match* val (unpack match-body))))
{:-> ->* {:-> ->*
:->> ->>* :->> ->>*
:-?> -?>* :-?> -?>*