Nicolas Cellier uploaded a new version of Compiler to project The Inbox:
http://source.squeak.org/inbox/Compiler-nice.280.mcz ==================== Summary ==================== Name: Compiler-nice.280 Author: nice Time: 24 February 2014, 10:41:41.999 pm UUID: 6e62064c-bd88-4957-98e4-c57da15738fc Ancestors: Compiler-nice.279 Accept ^ as a binary selector With this change, binary selector can be arbitrarily composed of #verticalBar | #upArrow ^ or any other Character classified as #binary (See Scanner class>>initializeTypeTable) =============== Diff against Compiler-nice.279 =============== Item was changed: ----- Method: Parser>>messagePart:repeat: (in category 'expression types') ----- messagePart: level repeat: repeat | start receiver selector args precedence words keywordStart | [receiver := parseNode. (hereType == #keyword and: [level >= 3]) ifTrue: [start := self startOfNextToken. selector := WriteStream on: (String new: 32). args := OrderedCollection new. words := OrderedCollection new. [hereType == #keyword] whileTrue: [keywordStart := self startOfNextToken + requestorOffset. selector nextPutAll: self advance. words addLast: (keywordStart to: self endOfLastToken + requestorOffset). self primaryExpression ifFalse: [^self expected: 'Argument']. self messagePart: 2 repeat: true. args addLast: parseNode]. (Symbol hasInterned: selector contents ifTrue: [ :sym | selector := sym]) ifFalse: [ selector := self correctSelector: selector contents wordIntervals: words exprInterval: (start to: self endOfLastToken) ifAbort: [ ^ self fail ] ]. precedence := 3] ifFalse: [ + (level >= 2 and: [hereType == #verticalBar or: [hereType == #upArrow]]) ifTrue: [self transformVerticalBarAndUpArrowIntoABinarySelector]. - (level >= 2 and: [hereType == #verticalBar]) ifTrue: [self transformAVerticalBarIntoABinarySelector]. (hereType == #binary and: [level >= 2]) ifTrue: [start := self startOfNextToken. selector := self advance asOctetString asSymbol. self primaryExpression ifFalse: [^self expected: 'Argument']. self messagePart: 1 repeat: true. args := Array with: parseNode. precedence := 2] ifFalse: [hereType == #word ifTrue: [start := self startOfNextToken. selector := self advance. args := #(). words := OrderedCollection with: (start + requestorOffset to: self endOfLastToken + requestorOffset). (Symbol hasInterned: selector ifTrue: [ :sym | selector := sym]) ifFalse: [ selector := self correctSelector: selector wordIntervals: words exprInterval: (start to: self endOfLastToken) ifAbort: [ ^ self fail ] ]. precedence := 1] ifFalse: [^args notNil]]]. parseNode := MessageNode new receiver: receiver selector: selector arguments: args precedence: precedence from: encoder sourceRange: (start to: self endOfLastToken). repeat] whileTrue: []. ^true! Item was changed: ----- Method: Parser>>pattern:inContext: (in category 'expression types') ----- pattern: fromDoit inContext: ctxt " unarySelector | binarySelector arg | keyword arg {keyword arg} => {selector, arguments, precedence}." | args selector | doitFlag := fromDoit. fromDoit ifTrue: [^ctxt == nil ifTrue: [{#DoIt. {}. 1}] ifFalse: [{#DoItIn:. {encoder encodeVariable: encoder doItInContextName}. 3}]]. hereType == #word ifTrue: [^ {self advance asSymbol. {}. 1}]. + (hereType == #verticalBar or: [hereType == #upArrow]) ifTrue: [self transformVerticalBarAndUpArrowIntoABinarySelector]. + hereType == #binary ifTrue: - (hereType == #binary or: [hereType == #verticalBar]) ifTrue: [selector := self advance asSymbol. args := Array with: (encoder bindArg: self argumentName). ^ {selector. args. 2}]. hereType == #keyword ifTrue: [selector := WriteStream on: (String new: 32). args := OrderedCollection new. [hereType == #keyword] whileTrue:[ selector nextPutAll: self advance. args addLast: (encoder bindArg: self argumentName). ]. ^ {selector contents asSymbol. args. 3}]. ^self expected: 'Message pattern'! Item was removed: - ----- Method: Parser>>transformAVerticalBarIntoABinarySelector (in category 'scanning') ----- - transformAVerticalBarIntoABinarySelector - "Transform a vertical bar into a binary selector. - Eventually aggregate a serie of immediately following vertical bars and a binary selector. - Note that this aggregation cannot occur at scan time, because a pair of vertical bars can be encountered in two valid constructs: - - either as an empty temporaries specification, - - or as a local temporaries specification in a block of arity > 0" - here := '|'. - hereType := #binary. - [tokenType == #verticalBar and: [hereMark + here size = mark]] - whileTrue: [ - here := here , '|'. - hereEnd := hereEnd + 1. - self scanToken]. - (tokenType == #binary and: [hereMark + here size = mark]) - ifTrue: [ - here := here asString , token. - hereType := #binary. - hereEnd := hereEnd + token size. - self scanToken].! Item was added: + ----- Method: Parser>>transformVerticalBarAndUpArrowIntoABinarySelector (in category 'scanning') ----- + transformVerticalBarAndUpArrowIntoABinarySelector + "Transform a vertical bar and or a up arrow into a binary selector. + Eventually aggregate a serie of immediately following vertical bars, up arrows and a binary selector. + Note that this aggregation cannot occur at scan time, because a pair of vertical bars can be encountered in two valid constructs: + - either as an empty temporaries specification, + - or as a local temporaries specification in a block of arity > 0" + here := String with: here. + hereType := #binary. + [(tokenType == #verticalBar or: [tokenType == #upArrow]) and: [hereMark + here size = mark]] + whileTrue: [ + here := here , (String with: token). + hereEnd := hereEnd + 1. + self scanToken]. + (tokenType == #binary and: [hereMark + here size = mark]) + ifTrue: [ + here := here asString , token. + hereType := #binary. + hereEnd := hereEnd + token size. + self scanToken].! Item was changed: ----- Method: Scanner>>xBinary (in category 'multi-character scans') ----- xBinary + | startOfToken type | - | startOfToken | tokenType := #binary. startOfToken := mark. token := String with: self step. + [(type := self typeTableAt: hereChar) == #xBinary or: [type == #verticalBar or: [type == #upArrow]]] whileTrue: - [(self typeTableAt: hereChar) == #xBinary or: [(self typeTableAt: hereChar) == #verticalBar]] whileTrue: [(hereChar == $- and: [(self typeTableAt: aheadChar) == #xDigit]) ifTrue: [^self ambiguousSelector: (token , '-') inRange: (startOfToken to: source position - 1).]. token := token, (String with: self step)]. token := token asSymbol! |
On 24 February 2014 13:41, <[hidden email]> wrote:
> Nicolas Cellier uploaded a new version of Compiler to project The Inbox: > http://source.squeak.org/inbox/Compiler-nice.280.mcz > > ==================== Summary ==================== > > Name: Compiler-nice.280 > Author: nice > Time: 24 February 2014, 10:41:41.999 pm > UUID: 6e62064c-bd88-4957-98e4-c57da15738fc > Ancestors: Compiler-nice.279 > > Accept ^ as a binary selector > With this change, binary selector can be arbitrarily composed of #verticalBar | #upArrow ^ or any other Character classified as #binary (See Scanner class>>initializeTypeTable) > > =============== Diff against Compiler-nice.279 =============== So foo | a | a := 1 + 3 ^ a. actually means foo | a | a := 1 + 3 ^ a. ^ self. Which might be surprising - currently the former will fail with a sensible error message. (I say this while liking that I can write "2 ^ 2" instead of "2 squared"...) frank |
2014-02-25 8:17 GMT+01:00 Frank Shearar <[hidden email]>:
Yep, this is the objection that I anticipated in my first annoucement. Note that the format is clearly expressing the intention, that makes me think that the Python's interpretation of formating is not that bad ;) but we ain't gonna change that... If #'^' is not a Symbol, then you'll get a warning that you used a new selector. I don't remember, are all 1 char Symbol still interned, or did we change that? Of course, once you implement ^ as a binary message, then the code is accepted as is without any sort of warning... It's the same for self which is interpreted as a message send in this code: x := 1 + self foo self bar: x. It warn once, at first self send, but then, #self is interned and you're never warned again... For this sort of error, we could have a kind of lint rule for checking indentation : a single tab after a cr should match a beginning of a new statement (same with proper indentation inside blocks)
Nicolas frank |
On 25.02.2014, at 03:43, Nicolas Cellier <[hidden email]> wrote:
This would make sense if we we switch to ↑ (U+2191) as return. - Bert - smime.p7s (5K) Download Attachment |
>
> This would make sense if we we switch to ↑ (U+2191) as return. I always wanted that, but how to input? signature.asc (1K) Download Attachment |
control + up arrow ? Cheers, Karl On Tue, Feb 25, 2014 at 3:24 PM, Tobias Pape <[hidden email]> wrote:
|
On 25.02.2014, at 20:52, karl ramberg <[hidden email]> wrote:
> control + up arrow ? Control should be reserved for controlling stuff, you would run into a lot of key-clashes on both, linux and osx (that I know of) but alt + up should be decent :) We can then think about alt-left for <- (aka := ) ;) Best -Tobias signature.asc (1K) Download Attachment |
On 25-02-2014, at 12:01 PM, Tobias Pape <[hidden email]> wrote: > On 25.02.2014, at 20:52, karl ramberg <[hidden email]> wrote: > >> control + up arrow ? > > Control should be reserved for controlling stuff, > you would run into a lot of key-clashes on both, linux and osx (that I know of) > but > alt + up > should be decent :) > We can then think about alt-left for <- (aka := ) ;) Y’see this is where it all gets silly. At some point you have to trade; you can have nice clear easy to explain or you can have complicated unintelligible and pointless. If you want the latter, go play with C++. If we use some form of up-arrow glyph, whether plain old ascii caret or unicode +2191, then we need a sensible way to type it on typical keyboards. Anyone thinking that I will happily go all emacs and type ctl-alt-meta2-lshift-stallman-cursor-up is going to be disappointed. And deaf in one ear after I finish shouting at them…. Similarly for whatever assignment symbol one chose - I really dislike the := pascal nonsense - we need a convenient keypress. I say stick with shift-^ and shift-_ and accept that they are removed from general character usage. Either we have single symbols for these two rather important syntactical elements and accept that this takes out of circulation for general character usage, or we have to do the pascal thing and use long winded nonsense; want your code to look like abstractBytecodeMessagesFrom: startpc to: endpc do: aBlock "Evaluate aBlock with the sequence of abstract bytecodes from startpc through endpc in the receiver" TEMP-DCL scanner scanner := InstructionStream new method: self pc: startpc. BEGIN-BLOCK scanner pc <= endpc END_BLOCK whileTrue: BEGIN-BLOCK BEGIN-BLOCK scanner interpretNextInstructionFor: nil END_BLOCK on: MessageNotUnderstood do: BEGIN-BLOCK TEMP-DCL ex aBlock value: ex message END_BLOCK END_BLOCK Hmm? Not my idea of nice. And besides, you could use *exactly the same argument* about ‘why not use all characters in names’ to demand to be allowed to use BEGIN-BLOCK as a variable name. So to (mis)quote Zaphod, “ten out of ten for cleverness, minus several million for style" tim -- tim Rowledge; [hidden email]; http://www.rowledge.org/tim Never write software that patronizes the user. |
Hi,
On 25.02.2014, at 23:35, tim Rowledge <[hidden email]> wrote: > On 25-02-2014, at 12:01 PM, Tobias Pape <[hidden email]> wrote: > >> On 25.02.2014, at 20:52, karl ramberg <[hidden email]> wrote: >> >>> control + up arrow ? >> >> Control should be reserved for controlling stuff, >> you would run into a lot of key-clashes on both, linux and osx (that I know of) >> but >> alt + up >> should be decent :) >> We can then think about alt-left for <- (aka := ) ;) > > Y’see this is where it all gets silly. > At some point you have to trade; you can have nice clear easy to explain or you can have complicated unintelligible and pointless. If you want the latter, go play with C++. > > If we use some form of up-arrow glyph, whether plain old ascii caret or unicode +2191, then we need a sensible way to type it on typical keyboards. Anyone thinking that I will happily go all emacs and type ctl-alt-meta2-lshift-stallman-cursor-up is going to be disappointed. And deaf in one ear after I finish shouting at them…. Similarly for whatever assignment symbol one chose - I really dislike the := pascal nonsense - we need a convenient keypress. I say stick with shift-^ and shift-_ and accept that they are removed from general character usage. This is too much for me to actually like the arrow glyphs. Apart from ^ and _ already requiring shift, losing those character would be more "meh" than "yay" for me. > Either we have single symbols for these two rather important syntactical elements and accept that this takes out of circulation for general character usage, or we have to do the pascal thing and use long winded nonsense; hey, we already have ctrl-shift-t and ctrl-shift-f inserting stuff in code… > want your code to look like > > abstractBytecodeMessagesFrom: startpc to: endpc do: aBlock > "Evaluate aBlock with the sequence of abstract bytecodes from startpc through endpc in the receiver" > TEMP-DCL scanner > scanner := InstructionStream new method: self pc: startpc. > BEGIN-BLOCK scanner pc <= endpc END_BLOCK whileTrue: > BEGIN-BLOCK > BEGIN-BLOCK > scanner interpretNextInstructionFor: nil > END_BLOCK > on: MessageNotUnderstood > do: BEGIN-BLOCK > TEMP-DCL ex > aBlock value: ex message > END_BLOCK > END_BLOCK > Hmm? Not my idea of nice. And besides, you could use *exactly the same argument* about ‘why not use all characters in names’ to demand to be allowed to use BEGIN-BLOCK as a variable name. > > So to (mis)quote Zaphod, “ten out of ten for cleverness, minus several million for style" Didn’t aim for cleverness, just wanted to say "whether doing it or not, this way will work less than that" Best -Tobias signature.asc (1K) Download Attachment |
In reply to this post by timrowledge
On 25 February 2014 14:35, tim Rowledge <[hidden email]> wrote:
> > On 25-02-2014, at 12:01 PM, Tobias Pape <[hidden email]> wrote: > >> On 25.02.2014, at 20:52, karl ramberg <[hidden email]> wrote: >> >>> control + up arrow ? >> >> Control should be reserved for controlling stuff, >> you would run into a lot of key-clashes on both, linux and osx (that I know of) >> but >> alt + up >> should be decent :) >> We can then think about alt-left for <- (aka := ) ;) > > Y’see this is where it all gets silly. At some point you have to trade; you can have nice clear easy to explain or you can have complicated unintelligible and pointless. If you want the latter, go play with C++. > > If we use some form of up-arrow glyph, whether plain old ascii caret or unicode +2191, then we need a sensible way to type it on typical keyboards. Anyone thinking that I will happily go all emacs and type ctl-alt-meta2-lshift-stallman-cursor-up is going to be disappointed. And deaf in one ear after I finish shouting at them…. Similarly for whatever assignment symbol one chose - I really dislike the := pascal nonsense - we need a convenient keypress. I say stick with shift-^ and shift-_ and accept that they are removed from general character usage. > > Either we have single symbols for these two rather important syntactical elements and accept that this takes out of circulation for general character usage, or we have to do the pascal thing and use long winded nonsense; want your code to look like > > abstractBytecodeMessagesFrom: startpc to: endpc do: aBlock > "Evaluate aBlock with the sequence of abstract bytecodes from startpc through endpc in the receiver" > TEMP-DCL scanner > scanner := InstructionStream new method: self pc: startpc. > BEGIN-BLOCK scanner pc <= endpc END_BLOCK whileTrue: > BEGIN-BLOCK > BEGIN-BLOCK > scanner interpretNextInstructionFor: nil > END_BLOCK > on: MessageNotUnderstood > do: BEGIN-BLOCK > TEMP-DCL ex > aBlock value: ex message > END_BLOCK > END_BLOCK > Hmm? Not my idea of nice. And besides, you could use *exactly the same argument* about ‘why not use all characters in names’ to demand to be allowed to use BEGIN-BLOCK as a variable name. Well, no, because Peter Landin solved this a long time ago (1966) with the offside rule :) > So to (mis)quote Zaphod, “ten out of ten for cleverness, minus several million for style" It's not clear to what you're objecting: Nicolas' turning ^ into a binary selector? Tobias' asking how we input some fancy unicode character? frank > tim > -- > tim Rowledge; [hidden email]; http://www.rowledge.org/tim > Never write software that patronizes the user. > > > |
In reply to this post by Tobias Pape
Hi Tobias - I wasn’t particularly aiming anything at what you had written, merely replying to the nearest convenient email; please don’t take offence!
On 25-02-2014, at 2:50 PM, Tobias Pape <[hidden email]> wrote: [snip] > > This is too much for me to actually like the arrow glyphs. Apart from ^ and _ already requiring shift, > losing those character would be more "meh" than "yay" for me. Likewise; I don’t see any real value in having _ and ^/ available for use in names when they have a reasonable place as markers for two very important concepts that need easily obtainable symbols. > >> Either we have single symbols for these two rather important syntactical elements and accept that this takes out of circulation for general character usage, or we have to do the pascal thing and use long winded nonsense; > > hey, we already have ctrl-shift-t and ctrl-shift-f inserting stuff in code… True, and ctl-shift-char is just barely within my idea of acceptable and, of course, it pretty much has to be that way since shift-f is a simple char and ctl-f has become strongly bound to ‘find’ in most people’s mental maps. >> >> So to (mis)quote Zaphod, “ten out of ten for cleverness, minus several million for style" > > Didn’t aim for cleverness, just wanted to say "whether doing it or not, this way will work less than that” email is a terrible medium for humour, especially 30 year old location and life-experience-specific humour. tim -- tim Rowledge; [hidden email]; http://www.rowledge.org/tim Useful random insult:- Always responds to "Make Money Fast" postings on the Net. |
In reply to this post by Frank Shearar-3
On 25-02-2014, at 2:56 PM, Frank Shearar <[hidden email]> wrote: > > It's not clear to what you're objecting: Nicolas' turning ^ into a > binary selector? Tobias' asking how we input some fancy unicode > character? The former, along with whining piteously about the loss of the left arrow assign. I’d be quite happy with Bert’s suggesion for using U+2191 for return as long as it was entered with something decently simple like shift-^ or at *very* worst ctl-shift-^. Err, without the dot. And without the 'dot Err', too. The likely practical issue would be the inevitable problems with some important or common tool failing to handle U+2191. tim -- tim Rowledge; [hidden email]; http://www.rowledge.org/tim Foolproof operation: All parameters are hard coded. |
In reply to this post by timrowledge
On 26.02.2014, at 00:04, tim Rowledge <[hidden email]> wrote:
> Hi Tobias - I wasn’t particularly aiming anything at what you had written, merely replying to the nearest convenient email; please don’t take offence! None taken :) > > > On 25-02-2014, at 2:50 PM, Tobias Pape <[hidden email]> wrote: > [snip] >> >> This is too much for me to actually like the arrow glyphs. Apart from ^ and _ already requiring shift, >> losing those character would be more "meh" than "yay" for me. > > Likewise; I don’t see any real value in having _ and ^/ available for use in names when they have a reasonable place as markers for two very important concepts that need easily obtainable symbols. but the unicode ones are nicerer!!!111einself </> Also, I would never say that _/^ should go away buy ↑ and ← used likewise, with certain shortcuts to input (we have no APL keyboard, after all, and although a little easier to access than PITA on OSX, it will would be double-PITA on, say, Windows) > >> >>> Either we have single symbols for these two rather important syntactical elements and accept that this takes out of circulation for general character usage, or we have to do the pascal thing and use long winded nonsense; >> >> hey, we already have ctrl-shift-t and ctrl-shift-f inserting stuff in code… > > True, and ctl-shift-char is just barely within my idea of acceptable and, of course, it pretty much has to be that way since shift-f is a simple char and ctl-f has become strongly bound to ‘find’ in most people’s mental maps. > >>> >>> So to (mis)quote Zaphod, “ten out of ten for cleverness, minus several million for style" >> >> Didn’t aim for cleverness, just wanted to say "whether doing it or not, this way will work less than that” > > email is a terrible medium for humour, especially 30 year old location and life-experience-specific humour. Best -Tobias signature.asc (1K) Download Attachment |
In reply to this post by timrowledge
On 25 February 2014 15:04, tim Rowledge <[hidden email]> wrote:
> Hi Tobias - I wasn’t particularly aiming anything at what you had written, merely replying to the nearest convenient email; please don’t take offence! > > > On 25-02-2014, at 2:50 PM, Tobias Pape <[hidden email]> wrote: > [snip] >> >> This is too much for me to actually like the arrow glyphs. Apart from ^ and _ already requiring shift, >> losing those character would be more "meh" than "yay" for me. > > Likewise; I don’t see any real value in having _ and ^/ available for use in names when they have a reasonable place as markers for two very important concepts that need easily obtainable symbols. Being able to use _ in names makes it _vastly_ easier to write wrappers to C-like APIs. I'll admit that the argument to supporting ^ as a binary selector's a much weaker argument. However, as long as it doesn't mess with the grammar too much - if the side effects aren't large - I'd rather be able to use ^ as a selector than not. frank |
In reply to this post by timrowledge
On 25 February 2014 15:09, tim Rowledge <[hidden email]> wrote:
> > On 25-02-2014, at 2:56 PM, Frank Shearar <[hidden email]> wrote: >> >> It's not clear to what you're objecting: Nicolas' turning ^ into a >> binary selector? Tobias' asking how we input some fancy unicode >> character? > > The former, along with whining piteously about the loss of the left arrow assign. I’d be quite happy with Bert’s suggesion for using U+2191 for return as long as it was entered with something decently simple like shift-^ or at *very* worst ctl-shift-^. Err, without the dot. And without the 'dot Err', too. > The likely practical issue would be the inevitable problems with some important or common tool failing to handle U+2191. Well, given that (AFAIK at least) we don't properly support Unicode in our own IDEs, I'd say ixnay on the U+2191, until that happy day we _do_ support Unicode properly in all our tools. Oh, and of course we need a proper input means (and it needs to be trivially findable), as you point out. frank > tim > -- > tim Rowledge; [hidden email]; http://www.rowledge.org/tim > Foolproof operation: All parameters are hard coded. > > > |
In reply to this post by timrowledge
On 26.02.2014, at 00:09, tim Rowledge <[hidden email]> wrote:
> On 25-02-2014, at 2:56 PM, Frank Shearar <[hidden email]> wrote: >> >> It's not clear to what you're objecting: Nicolas' turning ^ into a >> binary selector? Tobias' asking how we input some fancy unicode >> character? > > The former, along with whining piteously about the loss of the left arrow assign. I’d be quite happy with Bert’s suggesion for using U+2191 for return as long as it was entered with something decently simple like shift-^ how do you input shift-^? ^ is already shift-6 for me. shift-shift-6? > or at *very* worst ctl-shift-^. Err, without the dot. And without the 'dot Err', too. > The likely practical issue would be the inevitable problems with some important or common tool failing to handle U+2191. There was a change set once, that replaced ^ and _ in the scanner/compiler by the actual arrows but left ^ and _ alone otherwise... Best -Tobias |
On 25-02-2014, at 3:16 PM, Tobias Pape <[hidden email]> wrote: > > how do you input shift-^? ^ is already shift-6 for me. shift-shift-6? I have this vague recollection of some keyboards not having the caret over the 6, which perhaps explains the slip. Or perhaps not having the underscore over the dash? Not sure. tim -- tim Rowledge; [hidden email]; http://www.rowledge.org/tim Resistance is useless! (If << 1 ohm) |
Tim Rowledge wrote:
> I have this vague recollection of some keyboards not having the caret > over the 6, which perhaps explains the slip. Or perhaps not having the > underscore over the dash? Not sure. The keyboard I am typing this on, which follows a standard called ABNT-2, has the caret two keys to the right of "L" (with shift, the unshifted character on the key is "~"). This discussion is a bit odd as viewed in Celeste because half of the posts show arrows while the other half show caret and underline even when one is quoting the other. Ah: Tim's posts are windows-1252 while Frank's are UTF-8, for example. As far as I know, the Pascal style assignment appeared in Digitalk's Methods even though the PC CGA character set actually had a left arrow. But the manual made it clear that the audience was Pascal programmers and the first few examples were shown both in Pascal and Smalltalk with an effort to show how similar things were. That was several generations back and today's programmers only find C syntax familiar. When using syntax coloring, the possibility of having a caret being misinterpreted due to a missing dot is not as great as when the code is read in plain text. I had looked into this when I came up with the idea of having the caret be a binary selector in a simplified version of Self. The return was no longer special syntax but just a one argument message to implicit self. The corresponding "slot" held a continuation, so you didn't need a return bytecode. See item 2 in: http://www.merlintec.com/swiki/software/11.html -- Jecel |
Free forum by Nabble | Edit this page |