1 ! Copyright (C) 2005, 2006 Daniel Ehrenberg
\r
2 ! See http://factorcode.org/license.txt for BSD license.
\r
3 USING: io io.streams.string kernel math namespaces sequences
\r
4 strings circular prettyprint debugger unicode.categories ;
\r
7 ! * Basic underlying words
\r
8 ! Code stored in stdio
\r
9 ! Spot is composite so it won't be lost in sub-scopes
\r
10 TUPLE: spot char line column next ;
\r
14 : get-char ( -- char ) spot get spot-char ;
\r
15 : set-char ( char -- ) spot get set-spot-char ;
\r
16 : get-line ( -- line ) spot get spot-line ;
\r
17 : set-line ( line -- ) spot get set-spot-line ;
\r
18 : get-column ( -- column ) spot get spot-column ;
\r
19 : set-column ( column -- ) spot get set-spot-column ;
\r
20 : get-next ( -- char ) spot get spot-next ;
\r
21 : set-next ( char -- ) spot get set-spot-next ;
\r
24 TUPLE: parsing-error line column ;
\r
25 : <parsing-error> ( -- parsing-error )
\r
26 get-line get-column parsing-error construct-boa ;
\r
28 : construct-parsing-error ( ... slots class -- error )
\r
29 construct <parsing-error> over set-delegate ; inline
\r
31 : parsing-error. ( parsing-error -- )
\r
32 "Parsing error" print
\r
33 "Line: " write dup parsing-error-line .
\r
34 "Column: " write parsing-error-column . ;
\r
36 TUPLE: expected should-be was ;
\r
37 : <expected> ( should-be was -- error )
\r
38 { set-expected-should-be set-expected-was }
\r
39 expected construct-parsing-error ;
\r
42 "Token expected: " write dup expected-should-be print
\r
43 "Token present: " write expected-was print ;
\r
45 TUPLE: unexpected-end ;
\r
46 : <unexpected-end> ( -- unexpected-end )
\r
47 { } unexpected-end construct-parsing-error ;
\r
48 M: unexpected-end error.
\r
50 "File unexpectedly ended." print ;
\r
52 TUPLE: missing-close ;
\r
53 : <missing-close> ( -- missing-close )
\r
54 { } missing-close construct-parsing-error ;
\r
55 M: missing-close error.
\r
57 "Missing closing token." print ;
\r
61 ! * Basic utility words
\r
63 : record ( char -- )
\r
65 [ 0 get-line 1+ set-line ] [ get-column 1+ ] if
\r
68 : (next) ( -- char ) ! this normalizes \r\n and \r
\r
70 2dup swap CHAR: \r = [
\r
72 [ nip read1 ] [ nip CHAR: \n swap ] if
\r
74 set-next dup set-char ;
\r
79 <unexpected-end> throw
\r
84 get-char [ (next) record ] when ;
\r
86 : skip-until ( quot -- )
\r
89 [ call ] keep swap [ drop ] [
\r
92 ] [ drop ] if ; inline
\r
94 : take-until ( quot -- string )
\r
95 #! Take the substring of a string starting at spot
\r
96 #! from code until the quotation given is true and
\r
97 #! advance spot to after the substring.
\r
99 dup slip swap dup [ get-char , ] unless
\r
100 ] skip-until ] "" make nip ;
\r
102 : rest ( -- string )
\r
105 : take-char ( ch -- string )
\r
106 [ dup get-char = ] take-until nip ;
\r
108 : pass-blank ( -- )
\r
109 #! Advance code past any whitespace, including newlines
\r
110 [ get-char blank? not ] skip-until ;
\r
112 : string-matches? ( string circular -- ? )
\r
113 get-char over push-circular
\r
116 : take-string ( match -- string )
\r
117 dup length <circular-string>
\r
118 [ 2dup string-matches? ] take-until nip
\r
119 dup length rot length 1- - head
\r
120 get-char [ <missing-close> throw ] unless next ;
\r
123 get-char 2dup = [ 2drop ] [
\r
124 >r 1string r> 1string <expected> throw
\r
127 : expect-string ( string -- )
\r
128 dup [ drop get-char next ] map 2dup =
\r
129 [ 2drop ] [ <expected> throw ] if ;
\r
131 : init-parser ( -- )
\r
132 0 1 0 f <spot> spot set
\r
133 read1 set-next next ;
\r
135 : state-parse ( stream quot -- )
\r
136 ! with-stream implicitly creates a new scope which we use
\r
137 swap [ init-parser call ] with-stream ; inline
\r
139 : string-parse ( input quot -- )
\r
140 >r <string-reader> r> state-parse ; inline
\r