I've been playing with a new draft of my parser combinators,
and I want to deal with laziness properly while the codebase
is small so I have an idea of how to keep it working as it
grows. So I'm going to talk through what I've got and what
I'm thinking and feel free to jump in with a different idea
or way to go about this.
This code use my syntax extensions: https://codereview.stackexchange.com/questions/193520/an-enhanced-syntax-for-defining-functions-in-postscript
Code is a little wide, comments may wrap.
The function 'string-input' creates a lazy list of [char [row col]] structures in a lisp-like list of 2-element arrays for cons cells.
The /cdr/ part of the cons cell is a recursive invocation of string-input wrapped in an executable array.
`take' and 'drop' are the "API" functions for interacting with the
lazy list. Some helper functions like 'rest' come from the struct2 library.
(struct2.ps)run {
string-input{ r c s }{ % r c s -> ( [s0 [r c]] {[s1 [r1 c1]] ... })
s length 0 eq { null }{
[ s head [ r c ] ]
[ s head (\n) eq {r 1 add 0}{r c 1 add} ifelse
s rest /string-input cvx ] cvx
cons
} ifelse
} @func
take{ x n }{ n 0 eq { zero }{
/x load force dup zero ne { x-xs n 1 sub take cons } if
} ifelse } @func
drop{ n }{ n 0 gt {
next n 1 sub drop
} if } @func
force { dup xcheck { exec force } if }
next { dup second xcheck { dup 1 {force} update } if second } % force and update cdr
update { 3 copy pop get exch exec put } % a i p a[i]=p(a[i])
x-xs { dup first exch second }
head { 0 1 getinterval }
cons { 2 aa }
aa { array astore }
second { 1 get }
zero { null }
} pairs-begin
0 0 (abcd\ne) string-input
dup ==
9 take ==
0 0 (abcd\ne) string-input
dup 2 drop == ==
Output:
$ gsnd -q -dNOSAFER pc11a.ps
[[(a) [0 0]] {0 1 (bcd\ne) string-input}]
[[(a) [0 0]] [[(b) [0 1]] [[(c) [0 2]] [[(d) [0 3]] [[(\n) [0 4]] [[(e) [1 0]] null]]]]]]
[[(c) [0 2]] {0 3 (d\ne) string-input}]
[[(a) [0 0]] [[(b) [0 1]] [[(c) [0 2]] {0 3 (d\ne) string-input}]]]
So this much seems to be working. What I'd like to do now is write functions that can operate on "possibly lazy" data. I'm imagining a higher order function that takes an operation and wraps it in a lazy-safe wrapper that either does the operation of the data is not executable otherwise it wraps the data and operation into a new executable array and yield that.
The function I'd like to use this way is 'x-xs' which just dumps the
car and cdr on the stack.
x-xs { dup first exch second }
This actually seems too big to deal with all at once, so lets break it down.
x-xs { dup x_ exch xs_ } % needs cons cell to be real
x_ { first } % needs car to be real
xs_ { second } % needs cdr to be real
If the cons cell is *lazy* however, x-xs should instead wrap itself around
it in a new lazy value.
lazy-x-xs { { force dup x_ exch xs_ } curry } % when called, force inner value
And similarly (?) for x_ and xs_, they'll need to wrap themselves.
lazy-x_ { { first force } curry }
lazy-xs_ { { second force } curry }
pr better
lazy-xs_ { { next } curry }
'next' from above replaces the executable in the cdr with the real value
and then calls second. But I suppose I need a similar function to update
the car. Since my lazy list of characters only has lazy values in the
cdr, I kind of have a blind spot for dealing with laziness in the car.
Next there needs to be some decision layer to choose from the two behaviors.
?-x-xs { dup xcheck { lazy-x-xs }{ x-xs } ifelse }
?-x_ { dup xcheck { lazy-x_ }{ x_ } ifelse }
?-xs_ { dup xcheck { lazy-xs_ }{ xs_ } ifelse }
I know I'm missing some connections in there. But does this roughly
seem sensible? I'm think of adding it as a new syntax extension, like
@func. Maybe, @lazy. It could decorate a function with all the extra
business so ?-x-xs could be defined as
?-x-xs { dup x_ exch xs_ } @lazy
and it would be transformed into the above.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 292 |
Nodes: | 16 (2 / 14) |
Uptime: | 186:54:55 |
Calls: | 6,616 |
Files: | 12,165 |
Messages: | 5,314,904 |