]> gitweb.factorcode.org Git - factor.git/blob - core/parser/parser-docs.factor
Fixes #2966
[factor.git] / core / parser / parser-docs.factor
1 USING: arrays compiler.units definitions help.markup help.syntax
2 kernel lexer math namespaces quotations sequences source-files
3 strings vectors vocabs vocabs.parser words words.symbol ;
4 IN: parser
5
6 ARTICLE: "reading-ahead" "Reading ahead"
7 "Parsing words can consume input from the input stream. Words come in two flavors: words that throw upon finding end of file, and words that return " { $link f } " upon the same." $nl
8 "Parsing words that throw on end of file:"
9 { $subsections
10     scan-token
11     scan-word-name
12     scan-word
13     scan-datum
14     scan-number
15     scan-object
16 }
17 "Parsing words that return " { $link f } " on end of file:"
18 { $subsections
19     ?scan-token
20     ?scan-datum
21 }
22 "A simple example is the " { $link POSTPONE: \ } " word:"
23 { $see POSTPONE: \ } ;
24
25 ARTICLE: "parsing-word-nest" "Nested structure"
26 "Recall that the parser loop calls parsing words with an accumulator vector on the stack. The parser loop can be invoked recursively with a new, empty accumulator; the result can then be added to the original accumulator. This is how parsing words for object literals are implemented; object literals can nest arbitrarily deep."
27 $nl
28 "A simple example is the parsing word that reads a quotation:"
29 { $see POSTPONE: [ }
30 "This word uses a utility word which recursively invokes the parser, reading objects into a new accumulator until an occurrence of " { $link POSTPONE: ] } ":"
31 { $subsections parse-literal }
32 "There is another, lower-level word for reading nested structure, which is also useful when called directly:"
33 { $subsections parse-until }
34 "Words such as " { $link POSTPONE: ] } " use a declaration which causes them to throw an error when an unpaired occurrence is encountered:"
35 { $subsections POSTPONE: delimiter }
36 { $see-also POSTPONE: { POSTPONE: H{ POSTPONE: V{ POSTPONE: W{ POSTPONE: T{ POSTPONE: } } ;
37
38 ARTICLE: "defining-words" "Defining words"
39 "Defining words add definitions to the dictionary without modifying the parse tree. The simplest example is the " { $link POSTPONE: SYMBOL: } " word."
40 { $see POSTPONE: SYMBOL: }
41 "The key factor in the definition of " { $link POSTPONE: SYMBOL: } " is " { $link scan-new } ", which reads a token from the input and creates a word with that name. This word is then passed to " { $link define-symbol } "."
42 { $subsections
43     scan-new
44     scan-new-word
45 }
46 "Colon definitions are defined in a more elaborate way:"
47 { $subsections POSTPONE: : }
48 "The " { $link POSTPONE: : } " word first calls " { $link scan-new } ", and then reads input until reaching " { $link POSTPONE: ; } " using a utility word:"
49 { $subsections parse-definition }
50 "The " { $link POSTPONE: ; } " word is just a delimiter; an unpaired occurrence throws a parse error:"
51 { $see POSTPONE: ; }
52 "There are additional parsing words whose syntax is delimited by " { $link POSTPONE: ; } ", and they are all implemented by calling " { $link parse-definition } " or " { $link parse-array-def } "." ;
53
54 ARTICLE: "parsing-tokens" "Parsing raw tokens"
55 "So far we have seen how to read individual tokens, or read a sequence of parsed objects until a delimiter. It is also possible to read raw tokens from the input and perform custom processing."
56 $nl
57 "One example is the " { $link POSTPONE: USING: } " parsing word."
58 { $see POSTPONE: USING: }
59 "It reads a list of vocabularies terminated by " { $link POSTPONE: ; } ". However, the vocabulary names do not name words, except by coincidence; so " { $link parse-until } " cannot be used here. Instead, a set of lower-level combinators can be used:"
60 { $subsections
61     each-token
62     map-tokens
63     parse-tokens
64 } ;
65
66 ARTICLE: "parsing-words" "Parsing words"
67 "The Factor parser follows a simple recursive-descent design. The parser reads successive tokens from the input; if the token identifies a number or an ordinary word, it is added to an accumulator vector. Otherwise if the token identifies a parsing word, the parsing word is executed immediately."
68 $nl
69 "Parsing words are defined using the defining word:"
70 { $subsections POSTPONE: SYNTAX: }
71 "Parsing words have uppercase names by convention. Here is the simplest possible parsing word; it prints a greeting at parse time:"
72 { $code "SYNTAX: HELLO \"Hello world\" print ;" }
73 "Parsing words must not pop or push items from the stack; however, they are permitted to access the accumulator vector supplied by the parser at the top of the stack. That is, parsing words must have stack effect " { $snippet "( accum -- accum )" } ", where " { $snippet "accum" } " is the accumulator vector supplied by the parser."
74 $nl
75 "Parsing words can read input, add word definitions to the dictionary, and do anything an ordinary word can."
76 $nl
77 "Because of the stack restriction, parsing words cannot pass data to other words by leaving values on the stack; instead, use " { $link suffix! } " to add the data to the parse tree so that it can be evaluated later."
78 $nl
79 "Parsing words cannot be called from the same source file where they are defined, because new definitions are only compiled at the end of the source file. An attempt to use a parsing word in its own source file raises an error:"
80 { $subsections staging-violation }
81 "Tools for implementing parsing words:"
82 { $subsections
83     "reading-ahead"
84     "parsing-word-nest"
85     "defining-words"
86     "parsing-tokens"
87     "word-search-parsing"
88 } ;
89
90 ARTICLE: "top-level-forms" "Top level forms"
91 "Any code outside of a definition is known as a " { $emphasis "top-level form" } "; top-level forms are run after the entire source file has been parsed, regardless of their position in the file."
92 $nl
93 "Top-level forms cannot access the parse-time manifest (" { $link "word-search-parsing" } "), nor do they run inside " { $link with-compilation-unit } "; as a result, meta-programming might require extra work in a top-level form compared with a parsing word."
94 $nl
95 "Also, top-level forms run in a new dynamic scope, so using " { $link set } " to store values is almost always wrong, since the values will be lost after the top-level form completes. To save values computed by a top-level form, either use " { $link set-global } " or define a new word with the value." ;
96
97 ARTICLE: "parser" "The parser"
98 "The Factor parser reads textual representations of objects and definitions, with all syntax determined by " { $link "parsing-words" } ". The parser is implemented in the " { $vocab-link "parser" } " vocabulary, with standard syntax in the " { $vocab-link "syntax" } " vocabulary. See " { $link "syntax" } " for a description of standard syntax."
99 $nl
100 "The parser cross-references " { $link "source-files" } " and " { $link "definitions" } ". This functionality is used for improved error checking, as well as tools such as " { $link "tools.crossref" } " and " { $link "editor" } "."
101 $nl
102 "The parser can be invoked reflectively, to run strings and source files."
103 { $subsections
104     "eval"
105     run-file
106     parse-file
107 }
108 "If Factor is run from the command line with a script file supplied as an argument, the script is run using " { $link run-file } ". See " { $link "command-line" } "."
109 $nl
110 "While " { $link run-file } " can be used interactively in the listener to load user code into the session, this should only be done for quick one-off scripts, and real programs should instead rely on the automatic " { $link "vocabs.loader" } "."
111 { $see-also "parsing-words" "definitions" "definition-checking" } ;
112
113 ABOUT: "parser"
114
115 HELP: location
116 { $values { "loc" "a " { $snippet "{ path line# }" } " pair" } }
117 { $description "Outputs the current parser location. This value can be passed to " { $link set-where } " or " { $link remember-definition } "." } ;
118
119 HELP: save-location
120 { $values { "definition" "a definition specifier" } }
121 { $description "Saves the location of a definition and associates this definition with the current source file." } ;
122
123 HELP: bad-number
124 { $error-description "Indicates the parser encountered an invalid numeric literal." } ;
125
126 HELP: create-word-in
127 { $values { "str" "a word name" } { "word" "a new word" } }
128 { $description "Creates a word in the current vocabulary. Until re-defined, the word throws an error when invoked." }
129 $parsing-note ;
130
131 HELP: scan-new
132 { $values { "word" word } }
133 { $description "Reads the next token from the parser input, and creates a word with that name in the current vocabulary." }
134 { $errors "Throws an error if the end of the file is reached." }
135 $parsing-note ;
136
137 HELP: scan-new-word
138 { $values { "word" word } }
139 { $description "Reads the next token from the parser input, and creates a word with that name in the current vocabulary and resets the generic word properties of that word." }
140 { $errors "Throws an error if the end of the file is reached." }
141 $parsing-note ;
142
143 HELP: no-word-error
144 { $error-description "Thrown if the parser encounters a token which does not name a word in the current vocabulary search path. If any words with this name exist in vocabularies not part of the search path, a number of restarts will offer to add those vocabularies to the search path and use the chosen word." }
145 { $notes "Apart from a missing " { $link POSTPONE: USE: } ", this error can also indicate an ordering issue. In Factor, words must be defined before they can be called. Mutual recursion can be implemented via " { $link POSTPONE: DEFER: } "." } ;
146
147 HELP: no-word
148 { $values { "name" string } { "newword" word } }
149 { $description "Throws a " { $link no-word-error } "." } ;
150
151 HELP: parse-word
152 { $values { "string" string } { "word" word } }
153 { $description "The current vocabulary search path is searched for all words named by the " { $snippet "string" } ". If no words matches, an error is thrown, if one word matches, and it is already loaded, that word is returned. Otherwise throws a restartable error to let the user choose which word to use." }
154 { $errors "Throws a " { $link no-word-error } " if the string doesn't name a word." }
155 { $notes "This word is used to implement " { $link scan-word } "." } ;
156
157 HELP: parse-datum
158 { $values { "string" string } { "word/number" { $or word number } } }
159 { $description "If " { $snippet "string" } " is a valid number literal, it is converted to a number, otherwise the current vocabulary search path is searched for a word named by the string." }
160 { $errors "Throws an error if the token does not name a word, and does not parse as a number." }
161 { $notes "This word is used to implement " { $link ?scan-datum } " and " { $link scan-datum } "." } ;
162
163 HELP: scan-word
164 { $values { "word" word } }
165 { $description "Reads the next token from parser input. If the token is a valid number literal, it is converted to a number, otherwise the vocabulary search path is searched for a word named by the token." }
166 { $errors "Throws an error if the token does not name a word or end of file is reached." }
167 $parsing-note ;
168
169 { scan-word parse-word } related-words
170
171 HELP: scan-word-name
172 { $values { "string" string } }
173 { $description "Reads the next token from parser input and makes sure it does not parse as a number." }
174 { $errors "Throws an error if the scanned token is a number or upon finding end of file." }
175 $parsing-note ;
176
177 HELP: ?scan-datum
178 { $values { "word/number/f" { $maybe word number } } }
179 { $description "Reads the next token from parser input. If the token is found in the vocabulary search path, returns the word named by the token. If the token does not find a word, it is next converted to a number. If this conversion fails, too, this word returns " { $link f } "." }
180 $parsing-note ;
181
182 HELP: scan-datum
183 { $values { "word/number" { $or word number } } }
184 { $description "Reads the next token from parser input. If the token is found in the vocabulary search path, returns the word named be the token. If the token is not found in the vocabulary search path, it is converted to a number. If this conversion fails, an error is thrown." }
185 { $errors "Throws an error if the token is not a number or end of file is reached." }
186 $parsing-note ;
187
188 HELP: scan-number
189 { $values { "number" number } }
190 { $description "Reads the next token from parser input. If the token is a number literal, it is converted to a number. Otherwise, it throws an error." }
191 { $errors "Throws an error if the token is not a number or end of file is reached." }
192 $parsing-note ;
193
194 HELP: parse-until-step
195 { $values { "accum" vector } { "end" word } { "?" boolean } }
196 { $description "Parses a token. If the token is a number or an ordinary word, it is added to the accumulator. If it is a parsing word, calls the parsing word with the accumulator on the stack. Outputs " { $link f } " if " { $snippet "end" } " is encountered, " { $link t } " otherwise." }
197 $parsing-note ;
198
199 HELP: (parse-until)
200 { $values { "accum" vector } { "end" word } }
201 { $description "Parses objects from parser input until " { $snippet "end" } " is encountered, adding them to the accumulator." }
202 $parsing-note ;
203
204 HELP: parse-until
205 { $values { "end" word } { "vec" "a new vector" } }
206 { $description "Parses objects from parser input until " { $snippet "end" } ". Outputs a new vector with the results." }
207 { $examples "This word is used to implement " { $link POSTPONE: ARTICLE: } "." }
208 $parsing-note ;
209
210 { parse-tokens each-token map-tokens (parse-until) parse-until } related-words
211
212 HELP: (parse-lines)
213 { $values { "lexer" lexer } { "quot" "a new " { $link quotation } } }
214 { $description "Parses Factor source code using a custom lexer. The vocabulary search path is taken from the current scope." }
215 { $errors "Throws a " { $link lexer-error } " if the input is malformed." } ;
216
217 HELP: parse-lines
218 { $values { "lines" { $sequence string } } { "quot" "a new " { $link quotation } } }
219 { $description "Parses Factor source code which has been tokenized into lines. The vocabulary search path is taken from the current scope." }
220 { $errors "Throws a " { $link lexer-error } " if the input is malformed." } ;
221
222 HELP: parse-literal
223 { $values { "accum" vector } { "end" word } { "quot" { $quotation ( seq -- obj ) } } }
224 { $description "Parses objects from parser input until " { $snippet "end" } ", applies the quotation to the resulting sequence, and adds the output value to the accumulator." }
225 { $examples "This word is used to implement " { $link POSTPONE: [ } "." }
226 $parsing-note ;
227
228 HELP: parse-definition
229 { $values { "quot" "a new " { $link quotation } } }
230 { $description "Parses objects from parser input until " { $link POSTPONE: ; } " and outputs a quotation with the results." }
231 { $examples "This word is used to implement " { $link POSTPONE: : } "." }
232 $parsing-note ;
233
234 HELP: parse-array-def
235 { $values { "array" "a new " { $link array } } }
236 { $description "Like " { $link parse-definition } ", except the parsed sequence it outputted as an array." }
237 $parsing-note ;
238
239 HELP: bootstrap-syntax
240 { $var-description "Only set during bootstrap. Stores a copy of the " { $link vocab-words-assoc } " of the host's syntax vocabulary; this allows the host's parsing words to be used during bootstrap source parsing, not the target's." } ;
241
242 HELP: with-file-vocabs
243 { $values { "quot" quotation } }
244 { $description "Calls the quotation in a scope with an initial vocabulary search path consisting of just the " { $snippet "syntax" } " vocabulary." } ;
245
246 HELP: parse-fresh
247 { $values { "lines" { $sequence string } } { "quot" quotation } }
248 { $description "Parses Factor source code in a sequence of lines. The initial vocabulary search path is used (see " { $link with-file-vocabs } ")." }
249 { $errors "Throws a parse error if the input is malformed." } ;
250
251 HELP: filter-moved
252 { $values { "set1" set } { "set2" set } { "seq" { $sequence "definitions" } } }
253 { $description "Removes all definitions from " { $snippet "set2" } " which are in " { $snippet "set1" } " or are no longer present in the " { $link current-source-file } "." } ;
254
255 HELP: forget-smudged
256 { $description "Forgets removed definitions." } ;
257
258 HELP: finish-parsing
259 { $values { "lines" "the lines of text just parsed" } { "quot" "the quotation just parsed" } }
260 { $description "Records information to the " { $link current-source-file } "." }
261 { $notes "This is one of the factors of " { $link parse-stream } "." } ;
262
263 HELP: parse-stream
264 { $values { "stream" "an input stream" } { "name" "a file name for error reporting and cross-referencing" } { "quot" quotation } }
265 { $description "Parses Factor source code read from the stream. The initial vocabulary search path is used." }
266 { $errors "Throws an I/O error if there was an error reading from the stream. Throws a parse error if the input is malformed." } ;
267
268 HELP: parse-file
269 { $values { "path" "a pathname string" } { "quot" quotation } }
270 { $description "Parses the Factor source code stored in a file. The initial vocabulary search path is used." }
271 { $errors "Throws an I/O error if there was an error reading from the file. Throws a parse error if the input is malformed." } ;
272
273 HELP: run-file
274 { $values { "path" "a pathname string" } }
275 { $description "Parses the Factor source code stored in a file and runs it. The initial vocabulary search path is used." }
276 { $errors "Throws an error if loading the file fails, there input is malformed, or if a runtime error occurs while calling the parsed quotation." } ;
277
278 HELP: ?run-file
279 { $values { "path" "a pathname string" } }
280 { $description "If the file exists, runs it with " { $link run-file } ", otherwise does nothing." } ;
281
282 HELP: staging-violation
283 { $values { "word" word } }
284 { $description "Throws a " { $link staging-violation } " error." }
285 { $error-description "Thrown by the parser if a parsing word is used in the same compilation unit as where it was defined; see " { $link "compilation-units" } "." }
286 { $notes "One possible workaround is to use the " { $link POSTPONE: << } " word to execute code at parse time. However, executing words defined in the same source file at parse time is still prohibited." } ;
287
288 HELP: auto-use?
289 { $var-description "If set to a true value, the behavior of the parser when encountering an unknown word name is changed. If only one loaded vocabulary has a word with this name, instead of throwing an error, the parser adds the vocabulary to the search path and prints a parse note. Off by default." }
290 { $notes "This feature is intended to help during development. To generate a " { $link POSTPONE: USING: } " form automatically, enable " { $link auto-use? } ", load the source file, and copy and paste the " { $link POSTPONE: USING: } " form printed by the parser back into the file, then disable " { $link auto-use? } ". See " { $link "word-search-errors" } "." } ;
291
292 HELP: use-first-word?
293 { $values { "words" sequence } { "?" boolean } }
294 { $description "Checks if the first word can be used automatically without first throwing a restartable " { $link no-word-error } } ;
295
296 HELP: scan-object
297 { $values { "object" object } }
298 { $description "Parses a literal representation of an object." }
299 $parsing-note ;