diff --git a/core/alien/alien-docs.factor b/core/alien/alien-docs.factor old mode 100644 new mode 100755 index 259d78f67f..089091bec5 --- a/core/alien/alien-docs.factor +++ b/core/alien/alien-docs.factor @@ -70,7 +70,18 @@ HELP: load-library HELP: add-library { $values { "name" "a string" } { "path" "a string" } { "abi" "one of " { $snippet "\"cdecl\"" } " or " { $snippet "\"stdcall\"" } } } { $description "Defines a new logical library named " { $snippet "name" } " located in the file system at " { $snippet "path" } "and the specified ABI." } -{ $examples { $code "\"gif\" \"libgif.so\" \"cdecl\" add-library" } } ; +{ $notes "Because the entire source file is parsed before top-level forms are executed, " { $link add-library } " cannot be used in the same file as " { $link POSTPONE: FUNCTION: } " definitions from that library. The " { $link add-library } " call will happen too late, after compilation, and the alien calls will not work." +$nl +"Instead, " { $link add-library } " calls must either be placed in different source files from those that use that library, or alternatively, " { $link "syntax-immediate" } " can be used to load the library before compilation." } +{ $examples "Here is a typical usage of " { $link add-library } ":" +{ $code + "<< \"freetype\" {" + " { [ macosx? ] [ \"libfreetype.6.dylib\" \"cdecl\" add-library ] }" + " { [ windows? ] [ \"freetype6.dll\" \"cdecl\" add-library ] }" + " { [ t ] [ drop ] }" + "} cond >>" +} +"Note the parse time evaluation with " { $link POSTPONE: << } "." } ; HELP: alien-invoke-error { $error-description "Thrown if the word calling " { $link alien-invoke } " was not compiled with the optimizing compiler. This may be a result of one of several failure conditions:" diff --git a/core/classes/mixin/mixin-docs.factor b/core/classes/mixin/mixin-docs.factor index 3646b700b0..b0d02c8ecc 100755 --- a/core/classes/mixin/mixin-docs.factor +++ b/core/classes/mixin/mixin-docs.factor @@ -16,13 +16,13 @@ HELP: mixin-class HELP: define-mixin-class { $values { "class" word } } -{ $description "Defines a mixin class. This is the run-time equivalent of " { $link POSTPONE: MIXIN: } "." } +{ $description "Defines a mixin class. This is the run time equivalent of " { $link POSTPONE: MIXIN: } "." } { $notes "This word must be called from inside " { $link with-compilation-unit } "." } { $side-effects "class" } ; HELP: add-mixin-instance { $values { "class" class } { "mixin" class } } -{ $description "Defines a class to be an instance of a mixin class. This is the run-time equivalent of " { $link POSTPONE: INSTANCE: } "." } +{ $description "Defines a class to be an instance of a mixin class. This is the run time equivalent of " { $link POSTPONE: INSTANCE: } "." } { $notes "This word must be called from inside " { $link with-compilation-unit } "." } { $side-effects "class" } ; diff --git a/core/classes/predicate/predicate-docs.factor b/core/classes/predicate/predicate-docs.factor index 904807a9fb..2f340b353e 100755 --- a/core/classes/predicate/predicate-docs.factor +++ b/core/classes/predicate/predicate-docs.factor @@ -15,7 +15,7 @@ ABOUT: "predicates" HELP: define-predicate-class { $values { "superclass" class } { "class" class } { "definition" "a quotation with stack effect " { $snippet "( superclass -- ? )" } } } -{ $description "Defines a predicate class. This is the run-time equivalent of " { $link POSTPONE: PREDICATE: } "." } +{ $description "Defines a predicate class. This is the run time equivalent of " { $link POSTPONE: PREDICATE: } "." } { $notes "This word must be called from inside " { $link with-compilation-unit } "." } { $side-effects "class" } ; diff --git a/core/classes/union/union-docs.factor b/core/classes/union/union-docs.factor index aa620b0d34..ce5ad7b6fb 100755 --- a/core/classes/union/union-docs.factor +++ b/core/classes/union/union-docs.factor @@ -17,7 +17,7 @@ ABOUT: "unions" HELP: define-union-class { $values { "class" class } { "members" "a sequence of classes" } } -{ $description "Defines a union class with specified members. This is the run-time equivalent of " { $link POSTPONE: UNION: } "." } +{ $description "Defines a union class with specified members. This is the run time equivalent of " { $link POSTPONE: UNION: } "." } { $notes "This word must be called from inside " { $link with-compilation-unit } "." } { $side-effects "class" } ; diff --git a/core/compiler/compiler.factor b/core/compiler/compiler.factor index 5e8044f804..a18b832725 100755 --- a/core/compiler/compiler.factor +++ b/core/compiler/compiler.factor @@ -23,7 +23,6 @@ IN: compiler "compiled-effect" set-word-prop ; : (compile) ( word -- ) - yield [ dup word-dataflow optimize >r over dup r> generate ] [ @@ -37,8 +36,11 @@ IN: compiler [ [ 2drop t ] assoc-find 2drop dup ] keep delete-at ; : compile-loop ( assoc -- ) - dup assoc-empty? - [ drop ] [ dup delete-any (compile) compile-loop ] if ; + dup assoc-empty? [ drop ] [ + dup delete-any (compile) + yield + compile-loop + ] if ; : recompile ( words -- ) [ diff --git a/core/debugger/debugger.factor b/core/debugger/debugger.factor index a085eea0cb..7c973053da 100755 --- a/core/debugger/debugger.factor +++ b/core/debugger/debugger.factor @@ -227,3 +227,6 @@ M: forward-error error. M: undefined summary drop "Calling a deferred word before it has been defined" ; + +M: no-compilation-unit summary + drop "Defining a word outside of a compilation unit" ; diff --git a/core/definitions/definitions-docs.factor b/core/definitions/definitions-docs.factor index 791e5bef5e..f8eeafd505 100755 --- a/core/definitions/definitions-docs.factor +++ b/core/definitions/definitions-docs.factor @@ -62,7 +62,9 @@ $nl ARTICLE: "compilation-units" "Compilation units" "A " { $emphasis "compilation unit" } " scopes a group of related definitions. They are compiled and entered into the system in one atomic operation." $nl -"The parser groups all definitions in a source file into one compilation unit, and parsing words do not need to concern themselves with compilation units. However, if definitions are being created at run-time, a compilation unit must be created explicitly:" +"Words defined in a compilation unit may not be called until the compilation unit is finished. The parser detects this case for parsing words and throws a " { $link staging-violation } "; calling any other word from within its own compilation unit throws an " { $link undefined } " error." +$nl +"The parser groups all definitions in a source file into one compilation unit, and parsing words do not need to concern themselves with compilation units. However, if definitions are being created at run time, a compilation unit must be created explicitly:" { $subsection with-compilation-unit } "Words called to associate a definition with a source file location:" { $subsection remember-definition } @@ -163,8 +165,17 @@ HELP: forward-error HELP: with-compilation-unit { $values { "quot" quotation } } -{ $description "Calls a quotation in a new compilation unit. The quotation can define new words and classes, as well as forget words. When the quotation returns, any changed words are recompiled and applied atomically." } -{ $notes "Compilation units may be nested. The parser wraps every source file in a compilation unit, so parsing words may define new words without having to perform extra work; to define new words at any other time, you must wrap your defining code with this combinator." } ; +{ $description "Calls a quotation in a new compilation unit. The quotation can define new words and classes, as well as forget words. When the quotation returns, any changed words are recompiled, and changes are applied atomically." } +{ $notes "Compilation units may be nested." +$nl +"The parser wraps every source file in a compilation unit, so parsing words may define new words without having to perform extra work; to define new words at any other time, you must wrap your defining code with this combinator." +$nl +"Since compilation is relatively expensive, you should try to batch up as many definitions into one compilation unit as possible." } ; HELP: recompile-hook { $var-description "Quotation with stack effect " { $snippet "( words -- )" } ", called at the end of " { $link with-compilation-unit } "." } ; + +HELP: no-compilation-unit +{ $values { "word" word } } +{ $description "Throws a " { $link no-compilation-unit } " error." } +{ $error-description "Thrown when an attempt is made to define a word outside of a " { $link with-compilation-unit } " combinator." } ; diff --git a/core/parser/parser-docs.factor b/core/parser/parser-docs.factor index 9a2f0a1d22..ec061d0046 100755 --- a/core/parser/parser-docs.factor +++ b/core/parser/parser-docs.factor @@ -121,6 +121,8 @@ $nl { $code ": hello \"Hello world\" print ; parsing" } "Parsing words must have stack effect " { $snippet "( accum -- accum )" } ", where " { $snippet "accum" } " is the accumulator vector supplied by the parser. Parsing words can read input, add word definitions to the dictionary, and do anything an ordinary word can." $nl +"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:" +{ $link staging-violation } "Tools for implementing parsing words:" { $subsection "reading-ahead" } { $subsection "parsing-word-nest" } @@ -456,8 +458,8 @@ HELP: parse-fresh HELP: eval { $values { "str" string } } -{ $description "Parses Factor source code from a string, and calls the resulting quotation. The current vocabulary search path is used." } -{ $errors "Throws an error if the input is malformed, or if the quotation throws an error." } ; +{ $description "Parses Factor source code from a string, and calls the resulting quotation." } +{ $errors "Throws an error if the input is malformed, or if the evaluation itself throws an error." } ; HELP: outside-usages { $values { "seq" "a sequence of definitions" } { "usages" "an association list mapping definitions to sequences of definitions" } } @@ -505,3 +507,9 @@ HELP: bootstrap-file HELP: eval>string { $values { "str" string } { "output" string } } { $description "Evaluates the Factor code in " { $snippet "str" } " with the " { $link stdio } " stream rebound to a string output stream, then outputs the resulting string." } ; + +HELP: staging-violation +{ $values { "word" word } } +{ $description "Throws a " { $link staging-violation } " error." } +{ $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" } "." } +{ $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." } ; diff --git a/core/syntax/syntax-docs.factor b/core/syntax/syntax-docs.factor index a947362617..9f6509989b 100755 --- a/core/syntax/syntax-docs.factor +++ b/core/syntax/syntax-docs.factor @@ -28,6 +28,11 @@ ARTICLE: "syntax-comments" "Comments" { $subsection POSTPONE: ! } { $subsection POSTPONE: #! } ; +ARTICLE: "syntax-immediate" "Parse time evaluation" +"Code can be evaluated at parse time. This is a rarely-used feature; one use-case is " { $link "loading-libs" } ", where you want to execute some code before the words in a source file are compiled." +{ $subsection POSTPONE: << } +{ $subsection POSTPONE: >> } ; + ARTICLE: "syntax-integers" "Integer syntax" "The printed representation of an integer consists of a sequence of digits, optionally prefixed by a sign." { $code @@ -173,7 +178,8 @@ ARTICLE: "syntax" "Syntax" "Factor has two main forms of syntax: " { $emphasis "definition" } " syntax and " { $emphasis "literal" } " syntax. Code is data, so the syntax for code is a special case of object literal syntax. This section documents literal syntax. Definition syntax is covered in " { $link "words" } ". Extending the parser is the main topic of " { $link "parser" } "." { $subsection "parser-algorithm" } { $subsection "syntax-comments" } -{ $subsection "syntax-literals" } ; +{ $subsection "syntax-literals" } +{ $subsection "syntax-immediate" } ; ABOUT: "syntax" @@ -567,3 +573,14 @@ HELP: PRIVATE> { $description "Marks the end of a block of private word definitions." } ; { POSTPONE: } related-words + +HELP: << +{ $syntax "<< ... >>" } +{ $description "Evaluates some code at parse time." } +{ $notes "Calling words defined in the same source file at parse time is prohibited; see compilation unit as where it was defined; see " { $link "compilation-units" } "." } ; + +HELP: >> +{ $syntax ">>" } +{ $description "Marks the end of a parse time code block." } ; + +{ POSTPONE: << POSTPONE: >> } related-words diff --git a/core/tuples/tuples-docs.factor b/core/tuples/tuples-docs.factor index 012ea45384..49a0353dc5 100755 --- a/core/tuples/tuples-docs.factor +++ b/core/tuples/tuples-docs.factor @@ -144,7 +144,7 @@ HELP: check-tuple HELP: define-tuple-class { $values { "class" word } { "slots" "a sequence of strings" } } -{ $description "Defines a tuple class with slots named by " { $snippet "slots" } ". This is the run-time equivalent of " { $link POSTPONE: TUPLE: } "." } +{ $description "Defines a tuple class with slots named by " { $snippet "slots" } ". This is the run time equivalent of " { $link POSTPONE: TUPLE: } "." } { $notes "This word must be called from inside " { $link with-compilation-unit } "." } { $side-effects "class" } ; diff --git a/core/words/words-docs.factor b/core/words/words-docs.factor index 9c61bfdbd9..82dce8a241 100755 --- a/core/words/words-docs.factor +++ b/core/words/words-docs.factor @@ -54,7 +54,7 @@ ARTICLE: "primitives" "Primitives" { $subsection primitive? } ; ARTICLE: "deferred" "Deferred words and mutual recursion" -"Words cannot be referenced before they are defined; that is, source files must order definitions in a strictly bottom-up fashion. This is done to simplify the implementation, facilitate better parse-time checking and remove some odd corner cases; it also encourages better coding style. Sometimes this restriction gets in the way, for example when defining mutually-recursive words; one way to get around this limitation is to make a forward definition." +"Words cannot be referenced before they are defined; that is, source files must order definitions in a strictly bottom-up fashion. This is done to simplify the implementation, facilitate better parse time checking and remove some odd corner cases; it also encourages better coding style. Sometimes this restriction gets in the way, for example when defining mutually-recursive words; one way to get around this limitation is to make a forward definition." { $subsection POSTPONE: DEFER: } "The class of forward word definitions:" { $subsection deferred } @@ -248,13 +248,13 @@ $low-level-note HELP: define-symbol { $values { "word" word } } -{ $description "Defines the word to push itself on the stack when executed. This is the run-time equivalent of " { $link POSTPONE: SYMBOL: } "." } +{ $description "Defines the word to push itself on the stack when executed. This is the run time equivalent of " { $link POSTPONE: SYMBOL: } "." } { $notes "This word must be called from inside " { $link with-compilation-unit } "." } { $side-effects "word" } ; HELP: define-compound { $values { "word" word } { "def" quotation } } -{ $description "Defines the word to call a quotation when executed. This is the run-time equivalent of " { $link POSTPONE: : } "." } +{ $description "Defines the word to call a quotation when executed. This is the run time equivalent of " { $link POSTPONE: : } "." } { $notes "This word must be called from inside " { $link with-compilation-unit } "." } { $side-effects "word" } ;