! -*- factor -*-
! vim:set ft=factor:

! @@PLEAC@@_NAME
! @@SKIP@@ Factor

! @@PLEAC@@_WEB
! @@SKIP@@ http://factorcode.org/

! @@PLEAC@@_INTRO
! @@SKIP@@ Factor is a general purpose, dynamically typed, 
! @@SKIP@@ stack-based programming language released under a BSD-style license.

! @@SKIP@@ Factor has variables (global and local) but most of the time it is more
! @@SKIP@@ idiomatic to put results on the stack. The examples will look different
! @@SKIP@@ from the typical "a = b(x)" of other programming languages.

! @@PLEAC@@_1.0
! -----------------------------
: string ( -- str ) "\\n" ;                 ! two characters, \ and an n
: string ( -- str ) "Jon 'Maddog' Orwant" ; ! literal single quotes
! -----------------------------
: string ( -- str ) "\n" ;                      ! a "newline" character
: string ( -- str ) "Jon \"Maddog\" Orwant" ;   ! literal double quotes
! -----------------------------
! Multiline strings need the multiline vocabulary

USE: multiline

STRING: a
This is a multiline document
terminated by ; on a line by itself
;

: a ( -- str )
    <" This is a multiline document
terminated by double quotes plus greater sign"> ;
! -----------------------------

! @@PLEAC@@_1.1
! -----------------------------
! use subseq from the sequences vocabulary, since strings are just sequences
from to str subseq

1 3 "abcdefg" subseq .
! "bc"

! The rest from a given position is handled by tail
str n tail

1 "abcdefg" tail .
! "bcdefg"

! Replacing a substring with replace-slice
newstr from to str replace-slice

"1234" 3 7 "abcdefg" [ replace-slice ] keep .
!"abc1234"

: replace-tail ( new from seq -- )
    dup length swap [ replace-slice ] keep ;

"1234" 3 "abcdefg" replace-tail .
! "abc1234"

! -----------------------------

! split at five character boundaries
USE: splitting
5 group     ! Splits sequence/string on top of stack into an array

"abcdefghijklmnopqrstuvwxyz" 5 group .
! { "abcde" "fghij" "klmno" "pqrst" "uvwxy" "z" }

! chop string into individual characters
1 group

"abcd" 1 group .
! { "a" "b" "c" "d" }

! -----------------------------
: str ( -- str ) "This is what you have" ;
!                +012345678901234567890  Indexing forwards  (left to right)
!                 109876543210987654321- Indexing backwards (right to left)
!                  note that 0 means 10 or 20, etc. above

0 1 str subseq  ! "T" (on top of stack)
str 1 head      ! "T"
5 7 str subseq  ! "is"
str 13 tail     ! "you have"
str 1 tail*     ! "e"
str 4 tail*     ! "have"
! 8 indexed from backward, 3 characters
str length 8 - dup 3 + str subseq   ! "you"
! Or with a new word, using the locals vocabulary
USE: locals
:: chars-from-rindex ( from count str -- newstr )
    str length from - dup count + str subseq ;

8 3 str chars-from-rindex   ! "you"

! -----------------------------
SYMBOL: str
"This is what you have" str set

str get print
! This is what you have

str get
! Top of stack: "This is what you have"

[ 5 head ] [ 7 tail ] bi "wasn't" swap 3append   ! change "is" to "wasn't"
! Top of stack: "This wasn't what you have"

13 head "ondrous" append    ! replace last 12 characters
! Top of stack: "This wasn't wondrous"

1 tail      ! delete first character
! Top of stack: "his wasn't wondrous"

10 head*    ! delete last 10 characters
! Top of stack: "his wasn'"

str set
! str = "his wasn'"

str get print
! his wasn'
! -----------------------------
! you can test substrings with "subseq?"

10 tail* "substring" swap subseq?
    [ "\"substring\" within the last 10 characters" print ]
    when

! substitute "at" for "is", restricted to first five characters
USING: peg peg.search ;

5 cut >r "is" token [ drop "at" ] action replace r> append

! -----------------------------
! exchange the first and last letters in a string
"make a hat"
1 cut 1 cut* spin 3append
print
! take a ham

! -----------------------------

! @@PLEAC@@_1.3
! -----------------------------
swap
! -----------------------------

! @@PLEAC@@_1.15
! -----------------------------
! The "csv" vocabulary allows to parse real CSV

! reading the file "testdata.csv", UTF-8 encoded, CSV format
USE: csv

"testdata.csv" utf8 <file-reader> csv
! An array of row arrays is on the top of the stack now.

! -----------------------------
! You could use the word "<string-reader>" to turn a string into a stream.

USING: csv io.streams.string ;

"XYZZY,\"\",\"O'Reilly, Inc\",\"Wall, Larry\",\"a \"\"glug\"\" bit,\",5,\"Error, Core Dumped\""
    <string-reader> csv .
!    {
!        {
!            "XYZZY"
!            ""
!            "O'Reilly, Inc"
!            "Wall, Larry"
!            "a \"glug\" bit,"
!            "5"
!            "Error, Core Dumped"
!        }
!    }
! -----------------------------

! @@PLEAC@@_3.0
! -----------------------------
! All examples use the "calendar" vocabulary
USE: calendar

[ "Today is day " % now day-of-year # " of the current year." % ] "" make print
! Today is day 132 of the current year.
! -----------------------------

! @@PLEAC@@_3.1
! -----------------------------
now ! puts timestamp on stack

! accessing day
day>>
! -----------------------------

! Formatting the date with "calender.format" vocabulary
USE: calendar.format

now timestamp>rfc3339
! Top of stack: "2008-05-11T22:39:01+02:00"

now timestamp>ymd
! Top of stack: "2008-05-11"
! -----------------------------

! @@PLEAC@@_3.2
! -----------------------------
! Epoch milliseconds
now timestamp>millis
! Top of stack: 1210538827773

! Calculate seconds 
1000 / >integer
! -----------------------------

! GMT milliseconds
gmt timestamp>millis
! Top of Stack: 1210539018199

! @@PLEAC@@_3.3
! -----------------------------
! Epoch milliseconds to timestamp
1210538827773 millis>timestamp

! @@PLEAC@@_3.4
! -----------------------------
! Durations can be added and substracted with the words "time+" and "time-"
now 5 days time+ timestamp>ymd print
! 2008-05-17
now 5 days time- timestamp>ymd print
! 2008-05-07
! -----------------------------
1973 1 18 3 45 50 gmt-offset-duration <timestamp>   ! Birthtime
    0 0 55 2 17 5 <duration>                        ! Interval
    time+
    timestamp>string "Then is " swap append print
! Then is Wed, 14 Mar 1973 06:02:55
! -----------------------------
"Nat was 55 days old on: " 1973 1 18 <date> 55 days time+ timestamp>ymd append print
! Nat was 55 days old on: 1973-03-14
! -----------------------------

! @@PLEAC@@_3.5
! -----------------------------
! Timestamps can be substracted from each other with "time-". You get a duration,
! which can be converted to the number of seconds, minutes, etc.
now 5 days time-        ! 5 days before
    now 5 days time+    ! in 5 days
    time-               ! substracting the two timestamps
    dt>days .           ! converting duration to number of days and prettyprinting it.
! -10
! -----------------------------
! Putting duration since lunar landing onto the stack:
now
    1969 7 20 20 17 40 instant <timestamp>   ! 1969-07-20 20:17:40 UTC
    time-
! -----------------------------
USING: calendar math.parser ;
1981 6 16 4 35 25 instant <timestamp>   ! 16 Jun 1981, 4:35:25 UTC, Bree
1973 1 18 3 45 50 instant <timestamp>   ! 18 Jan 1973, 3:45:50 UTC, Nat
time-
dt>seconds
dup     ! Duplicates top of stack so we can use it later again.

[ "There were " % # " seconds between Nat and Bree" % ] "" make print
! There were 265337375 seconds between Nat and Bree

[ dup 60 mod dup , ! seconds
  - 60 /  ! rest in minutes
  dup 60 mod dup , ! minutes
  - 60 /  ! rest in hours
  dup 24 mod dup , ! hours
  - 24 /  ! rest in days
  dup 7 mod dup , ! days
  - 7 / , ! weeks
] { } make

[ [ "(" % # " weeks, " % # " days, " % # ":" % # ":" % # ")" % ] "" make ] with-datastack
first print
! (438 weeks, 5 days, 0:49:35)

! -----------------------------
1981 6 16 <date>    ! 16 Jun 1981, Bree
1973 1 18 <date>    ! 18 Jan 1973, Nat
time-
dt>days
[ "There were " % # " days between Nat and Bree" % ] "" make print
! There were 3071 days between Nat and Bree
! -----------------------------