]> gitweb.factorcode.org Git - factor.git/blob - core/parser/parser-docs.factor
Changing : foo ; parsing to SYNTAX: foo ;
[factor.git] / core / parser / parser-docs.factor
1 USING: help.markup help.syntax kernel sequences words
2 math strings vectors quotations generic effects classes
3 vocabs.loader definitions io vocabs source-files
4 quotations namespaces compiler.units assocs lexer
5 words.symbol words.alias words.constant vocabs.parser ;
6 IN: parser
7
8 ARTICLE: "reading-ahead" "Reading ahead"
9 "Parsing words can consume input:"
10 { $subsection scan }
11 { $subsection scan-word }
12 "For example, the " { $link POSTPONE: HEX: } " word uses this feature to read hexadecimal literals:"
13 { $see POSTPONE: HEX: }
14 "It is defined in terms of a lower-level word that takes the numerical base on the data stack, but reads the number from the parser and then adds it to the parse tree:"
15 { $see parse-base }
16 "Another simple example is the " { $link POSTPONE: \ } " word:"
17 { $see POSTPONE: \ } ;
18
19 ARTICLE: "parsing-word-nest" "Nested structure"
20 "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."
21 $nl
22 "A simple example is the parsing word that reads a quotation:"
23 { $see POSTPONE: [ }
24 "This word uses a utility word which recursively invokes the parser, reading objects into a new accumulator until an occurrence of " { $link POSTPONE: ] } ":"
25 { $subsection parse-literal }
26 "There is another, lower-level word for reading nested structure, which is also useful when called directly:"
27 { $subsection parse-until }
28 "Words such as " { $link POSTPONE: ] } " use a declaration which causes them to throw an error when an unpaired occurrence is encountered:"
29 { $subsection POSTPONE: delimiter }
30 { $see-also POSTPONE: { POSTPONE: H{ POSTPONE: V{ POSTPONE: W{ POSTPONE: T{ POSTPONE: } } ;
31
32 ARTICLE: "defining-words" "Defining words"
33 "Defining words add definitions to the dictionary without modifying the parse tree. The simplest example is the " { $link POSTPONE: SYMBOL: } " word."
34 { $see POSTPONE: SYMBOL: }
35 "The key factor in the definition of " { $link POSTPONE: SYMBOL: } " is " { $link CREATE } ", which reads a token from the input and creates a word with that name. This word is then passed to " { $link define-symbol } "."
36 { $subsection CREATE }
37 { $subsection CREATE-WORD }
38 "Colon definitions are defined in a more elaborate way:"
39 { $subsection POSTPONE: : }
40 "The " { $link POSTPONE: : } " word first calls " { $link CREATE } ", and then reads input until reaching " { $link POSTPONE: ; } " using a utility word:"
41 { $subsection parse-definition }
42 "The " { $link POSTPONE: ; } " word is just a delimiter; an unpaired occurrence throws a parse error:"
43 { $see POSTPONE: ; }
44 "There are additional parsing words whose syntax is delimited by " { $link POSTPONE: ; } ", and they are all implemented by calling " { $link parse-definition } "." ;
45
46 ARTICLE: "parsing-tokens" "Parsing raw tokens"
47 "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."
48 $nl
49 "One example is the " { $link POSTPONE: USING: } " parsing word."
50 { $see POSTPONE: USING: } 
51 "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 lower-level word is called:"
52 { $subsection parse-tokens } ;
53
54 ARTICLE: "parsing-words" "Parsing words"
55 "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."
56 $nl
57 "Parsing words are defined using the a defining word:"
58 { $subsection POSTPONE: SYNTAX: }
59 "Parsing words have uppercase names by convention. Here is the simplest possible parsing word; it prints a greeting at parse time:"
60 { $code "SYNTAX: HELLO \"Hello world\" print ;" }
61 "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."
62 $nl
63 "Parsing words can read input, add word definitions to the dictionary, and do anything an ordinary word can."
64 $nl
65 "Because of the stack restriction, parsing words cannot pass data to other words by leaving values on the stack; instead, use " { $link parsed } " to add the data to the parse tree so that it can be evaluated later."
66 $nl
67 "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:"
68 { $subsection staging-violation }
69 "Tools for implementing parsing words:"
70 { $subsection "reading-ahead" }
71 { $subsection "parsing-word-nest" }
72 { $subsection "defining-words" }
73 { $subsection "parsing-tokens" } ;
74
75 ARTICLE: "parser-files" "Parsing source files"
76 "The parser can run source files:"
77 { $subsection run-file }
78 { $subsection parse-file }
79 "The parser cross-references source files and definitions. This allows it to keep track of removed definitions, and prevent forward references and accidental redefinitions."
80 $nl
81 "While the above words are useful for one-off experiments, real programs should be written to use the vocabulary system instead; see " { $link "vocabs.loader" } "."
82 { $see-also "source-files" } ;
83
84 ARTICLE: "top-level-forms" "Top level forms"
85 "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."
86 $nl
87 "Top-level forms do not have access to the " { $link in } " and " { $link use } " variables that were set at parse time, nor do they run inside " { $link with-compilation-unit } "; so meta-programming might require extra work in a top-level form compared with a parsing word."
88 $nl
89 "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." ;
90
91 ARTICLE: "parser" "The parser"
92 "This parser is a general facility for reading textual representations of objects and definitions. The parser is implemented in the " { $vocab-link "parser" } " and " { $vocab-link "syntax" } " vocabularies."
93 $nl
94 "This section concerns itself with usage and extension of the parser. Standard syntax is described in " { $link "syntax" } "."
95 { $subsection "vocabulary-search" }
96 { $subsection "parser-files" }
97 { $subsection "top-level-forms" }
98 "The parser can be extended."
99 { $subsection "parsing-words" }
100 { $subsection "parser-lexer" }
101 "The parser can be invoked reflectively;"
102 { $subsection parse-stream }
103 { $see-also "definitions" "definition-checking" } ;
104
105 ABOUT: "parser"
106
107 HELP: location
108 { $values { "loc" "a " { $snippet "{ path line# }" } " pair" } }
109 { $description "Outputs the current parser location. This value can be passed to " { $link set-where } " or " { $link remember-definition } "." } ;
110
111 HELP: save-location
112 { $values { "definition" "a definition specifier" } }
113 { $description "Saves the location of a definition and associates this definition with the current source file." } ;
114
115 HELP: parser-notes
116 { $var-description "A boolean controlling whether the parser will print various notes and warnings. Switched on by default. If a source file is being run for its effect on " { $link output-stream } ", this variable should be switched off, to prevent parser notes from polluting the output." } ;
117
118 HELP: parser-notes?
119 { $values { "?" "a boolean" } }
120 { $description "Tests if the parser will print various notes and warnings. To disable parser notes, either set " { $link parser-notes } " to " { $link f } ", or pass the " { $snippet "-quiet" } " command line switch." } ;
121
122 HELP: bad-number
123 { $error-description "Indicates the parser encountered an invalid numeric literal." } ;
124
125 HELP: use
126 { $var-description "A variable holding the current vocabulary search path as a sequence of assocs." } ;
127
128 { use in use+ (use+) set-use set-in POSTPONE: USING: POSTPONE: USE: with-file-vocabs with-interactive-vocabs } related-words
129
130 HELP: in
131 { $var-description "A variable holding the name of the current vocabulary for new definitions." } ;
132
133 HELP: current-vocab
134 { $values { "str" "a vocabulary" } }
135 { $description "Returns the vocabulary stored in the " { $link in } " symbol. Throws an error if the current vocabulary is " { $link f } "." } ;
136
137 HELP: (use+)
138 { $values { "vocab" "an assoc mapping strings to words" } }
139 { $description "Adds an assoc at the front of the search path." }
140 $parsing-note ;
141
142 HELP: use+
143 { $values { "vocab" string } }
144 { $description "Adds a new vocabulary at the front of the search path after loading it if necessary. Subsequent word lookups by the parser will search this vocabulary first." }
145 $parsing-note
146 { $errors "Throws an error if the vocabulary does not exist." } ;
147
148 HELP: set-use
149 { $values { "seq" "a sequence of strings" } }
150 { $description "Sets the vocabulary search path. Later vocabularies take precedence." }
151 { $errors "Throws an error if one of the vocabularies does not exist." }
152 $parsing-note ;
153
154 HELP: add-use
155 { $values { "seq" "a sequence of strings" } }
156 { $description "Adds multiple vocabularies to the search path, with later vocabularies taking precedence." }
157 { $errors "Throws an error if one of the vocabularies does not exist." }
158 $parsing-note ;
159
160 HELP: set-in
161 { $values { "name" string } }
162 { $description "Sets the current vocabulary where new words will be defined, creating the vocabulary first if it does not exist." }
163 $parsing-note ;
164
165 HELP: create-in
166 { $values { "str" "a word name" } { "word" "a new word" } }
167 { $description "Creates a word in the current vocabulary. Until re-defined, the word throws an error when invoked." }
168 $parsing-note ;
169
170 HELP: CREATE
171 { $values { "word" word } }
172 { $description "Reads the next token from the line currently being parsed, and creates a word with that name in the current vocabulary." }
173 { $errors "Throws an error if the end of the line is reached." }
174 $parsing-note ;
175
176 HELP: no-word-error
177 { $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." }
178 { $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: } "." } ;
179
180 HELP: no-word
181 { $values { "name" string } { "newword" word } }
182 { $description "Throws a " { $link no-word-error } "." } ;
183
184 HELP: search
185 { $values { "str" string } { "word/f" "a word or " { $link f } } }
186 { $description "Searches for a word by name in the current vocabulary search path. If no such word could be found, outputs " { $link f } "." }
187 $parsing-note ;
188
189 HELP: scan-word
190 { $values { "word/number/f" "a word, number or " { $link f } } }
191 { $description "Reads the next token from parser input. If the token is a valid number literal, it is converted to a number, otherwise the dictionary is searched for a word named by the token. Outputs " { $link f } " if the end of the input has been reached." }
192 { $errors "Throws an error if the token does not name a word, and does not parse as a number." }
193 $parsing-note ;
194
195 HELP: parse-step
196 { $values { "accum" vector } { "end" word } { "?" "a boolean" } }
197 { $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." }
198 $parsing-note ;
199
200 HELP: (parse-until)
201 { $values { "accum" vector } { "end" word } }
202 { $description "Parses objects from parser input until " { $snippet "end" } " is encountered, adding them to the accumulator." }
203 $parsing-note ;
204
205 HELP: parse-until
206 { $values { "end" word } { "vec" "a new vector" } }
207 { $description "Parses objects from parser input until " { $snippet "end" } ". Outputs a new vector with the results." }
208 { $examples "This word is used to implement " { $link POSTPONE: ARTICLE: } "." }
209 $parsing-note ;
210
211 { parse-tokens (parse-until) parse-until } related-words
212
213 HELP: parsed
214 { $values { "accum" vector } { "obj" object } }
215 { $description "Convenience word for parsing words. It behaves exactly the same as " { $link push } ", except the accumulator remains on the stack." }
216 $parsing-note ;
217
218 HELP: (parse-lines)
219 { $values { "lexer" lexer } { "quot" "a new " { $link quotation } } }
220 { $description "Parses Factor source code using a custom lexer. The vocabulary search path is taken from the current scope." }
221 { $errors "Throws a " { $link lexer-error } " if the input is malformed." } ;
222
223 HELP: parse-lines
224 { $values { "lines" "a sequence of strings" } { "quot" "a new " { $link quotation } } }
225 { $description "Parses Factor source code which has been tokenized into lines. The vocabulary search path is taken from the current scope." }
226 { $errors "Throws a " { $link lexer-error } " if the input is malformed." } ;
227
228 HELP: parse-base
229 { $values { "base" "an integer between 2 and 36" } { "parsed" integer } }
230 { $description "Reads an integer in a specific numerical base from the parser input." }
231 $parsing-note ;
232
233 HELP: parse-literal
234 { $values { "accum" vector } { "end" word } { "quot" { $quotation "( seq -- obj )" } } }
235 { $description "Parses objects from parser input until " { $snippet "end" } ", applies the quotation to the resulting sequence, and adds the output value to the accumulator." }
236 { $examples "This word is used to implement " { $link POSTPONE: [ } "." }
237 $parsing-note ;
238
239 HELP: parse-definition
240 { $values { "quot" "a new " { $link quotation } } }
241 { $description "Parses objects from parser input until " { $link POSTPONE: ; } " and outputs a quotation with the results." }
242 { $examples "This word is used to implement " { $link POSTPONE: : } "." }
243 $parsing-note ;
244
245 HELP: bootstrap-syntax
246 { $var-description "Only set during bootstrap. Stores a copy of the " { $link vocab-words } " of the host's syntax vocabulary; this allows the host's parsing words to be used during bootstrap source parsing, not the target's." } ;
247
248 HELP: with-file-vocabs
249 { $values { "quot" quotation } }
250 { $description "Calls the quotation in a scope with the initial the vocabulary search path for parsing a file. This consists of just the " { $snippet "syntax" } " vocabulary." } ;
251
252 HELP: parse-fresh
253 { $values { "lines" "a sequence of strings" } { "quot" quotation } }
254 { $description "Parses Factor source code in a sequence of lines. The initial vocabulary search path is used (see " { $link with-file-vocabs } ")." }
255 { $errors "Throws a parse error if the input is malformed." } ;
256
257 HELP: filter-moved
258 { $values { "assoc1" assoc } { "assoc2" assoc } { "seq" "an seqence of definitions" } }
259 { $description "Removes all definitions from " { $snippet "assoc2" } " which are in " { $snippet "assoc1" } " or are are no longer present in the current " { $link file } "." } ;
260
261 HELP: forget-smudged
262 { $description "Forgets removed definitions and prints a warning message if any of them are still referenced from other source files." } ;
263
264 HELP: finish-parsing
265 { $values { "lines" "the lines of text just parsed" } { "quot" "the quotation just parsed" } }
266 { $description "Records information to the current " { $link file } " and prints warnings about any removed definitions which are still in use." }
267 { $notes "This is one of the factors of " { $link parse-stream } "." } ;
268
269 HELP: parse-stream
270 { $values { "stream" "an input stream" } { "name" "a file name for error reporting and cross-referencing" } { "quot" quotation } }
271 { $description "Parses Factor source code read from the stream. The initial vocabulary search path is used." }
272 { $errors "Throws an I/O error if there was an error reading from the stream. Throws a parse error if the input is malformed." } ;
273
274 HELP: parse-file
275 { $values { "file" "a pathname string" } { "quot" quotation } }
276 { $description "Parses the Factor source code stored in a file. The initial vocabulary search path is used." }
277 { $errors "Throws an I/O error if there was an error reading from the file. Throws a parse error if the input is malformed." } ;
278
279 HELP: run-file
280 { $values { "file" "a pathname string" } }
281 { $description "Parses the Factor source code stored in a file and runs it. The initial vocabulary search path is used." }
282 { $errors "Throws an error if loading the file fails, there input is malformed, or if a runtime error occurs while calling the parsed quotation." }  ;
283
284 HELP: ?run-file
285 { $values { "path" "a pathname string" } }
286 { $description "If the file exists, runs it with " { $link run-file } ", otherwise does nothing." } ;
287
288 HELP: staging-violation
289 { $values { "word" word } }
290 { $description "Throws a " { $link staging-violation } " error." }
291 { $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" } "." }
292 { $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." } ;
293
294 HELP: auto-use?
295 { $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." }
296 { $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 "vocabulary-search-errors" } "." } ;