syntax docs

cvs
Slava Pestov 2006-01-03 22:43:29 +00:00
parent 61196f79ff
commit b515bae5f4
15 changed files with 682 additions and 383 deletions

View File

@ -70,8 +70,6 @@ vectors words ;
"/library/io/files.factor"
"/library/io/binary.factor"
"/library/syntax/parse-words.factor"
"/library/syntax/parse-errors.factor"
"/library/syntax/parser.factor"
"/library/generic/generic.factor"
@ -89,14 +87,15 @@ vectors words ;
"/library/tools/describe.factor"
"/library/tools/debugger.factor"
"/library/syntax/parse-stream.factor"
"/library/tools/memory.factor"
"/library/tools/listener.factor"
"/library/tools/walker.factor"
"/library/tools/annotations.factor"
"/library/tools/inspector.factor"
"/library/syntax/parse-stream.factor"
"/library/test/test.factor"
@ -140,8 +139,6 @@ vectors words ;
"/library/io/buffer.factor"
"/library/syntax/generic.factor"
"/library/cli.factor"
"/library/bootstrap/init.factor"
@ -216,6 +213,9 @@ vectors words ;
"/library/collections/tree-each.facts"
"/library/collections/vectors.facts"
"/library/collections/virtual-sequences.facts"
"/library/syntax/parse-stream.facts"
"/library/syntax/parser.facts"
"/library/syntax/parse-syntax.facts"
"/library/syntax/parse-syntax.factor"

View File

@ -2,8 +2,8 @@
! See http://factor.sf.net/license.txt for BSD license.
IN: help
USING: arrays gadgets gadgets-panes gadgets-presentations
hashtables inspector io kernel lists namespaces prettyprint
sequences strings styles vectors words ;
generic hashtables inspector io kernel lists namespaces parser
prettyprint sequences strings styles vectors words ;
: uncons* dup first swap 1 swap tail ;
@ -72,10 +72,21 @@ M: simple-element print-element [ print-element ] each ;
: $code ( content -- )
first dup <input> [ format* ] ($code) ;
: $syntax ( word -- )
dup stack-effect [
"Syntax" $subheading
>r word-name $snippet " " $snippet r> $snippet
] [
drop
] if* ;
: $stack-effect ( word -- )
stack-effect [ "Stack effect" $subheading $snippet ] when* ;
: $synopsis ( content -- )
first dup
word-vocabulary [ "Vocabulary" $subheading $snippet ] when*
stack-effect [ "Stack effect" $subheading $snippet ] when*
dup parsing? [ $syntax ] [ $stack-effect ] if
terpri* ;
: $values ( content -- )
@ -95,11 +106,30 @@ M: simple-element print-element [ print-element ] each ;
: textual-list ( seq quot -- )
[ "," format* bl ] interleave ; inline
: $see ( content -- )
: $see-methods
"Methods defined in the generic word:" format* terpri
[ order word-sort ] keep
[ "methods" word-prop hash . ] curry
sequence-outliner ;
: $see-implementors
"Generic words defined for this class:" format* terpri
[ implementors word-sort ] keep
[ swap "methods" word-prop hash . ] curry
sequence-outliner ;
: ($see)
terpri*
code-style [ [ first see ] with-nesting* ] with-style
code-style [ with-nesting* ] with-style
terpri* ;
: $see ( content -- )
first {
{ [ dup class? ] [ $see-implementors ] }
{ [ dup generic? ] [ $see-methods ] }
{ [ t ] [ [ see ] ($see) ] }
} cond ;
: $example ( content -- )
first2 swap dup <input>
[
@ -163,3 +193,6 @@ DEFER: help
: $notes ( content -- )
"Notes" $subheading print-element ;
: $parsing-note
"This word should only be called from parsing words." $notes ;

View File

@ -122,7 +122,7 @@ ARTICLE: "tutorial-seq-combinators" "Sequences and combinators"
ARTICLE: "tutorial-rationals" "Numbers - integers and ratios"
"Factor supports arbitrary-precision integers and ratios. Try the following:"
{ $code ": factorial ( n -- n! ) 1 swap <range> product ;\n100 factorial .\n1 3 / 1 2 / + ." }
{ $code ": factorial ( n -- n! ) [ 1+ ] map product ;\n100 factorial .\n1 3 / 1 2 / + ." }
"Rational numbers are added, multiplied and reduced to lowest terms in the same way you learned in grade school." ;
ARTICLE: "tutorial-oop" "Object oriented programming"

View File

@ -1,50 +0,0 @@
! Copyright (C) 2004, 2005 Slava Pestov.
! See http://factor.sf.net/license.txt for BSD license.
! Bootstrapping trick; see doc/bootstrap.txt.
IN: !syntax
USING: arrays generic kernel lists namespaces parser sequences
syntax words ;
: GENERIC:
#! GENERIC: bar == G: bar simple-combination ;
CREATE dup reset-word define-generic ; parsing
: G:
#! G: word combination ;
CREATE dup reset-word [ define-generic* ] [ ] ; parsing
: UNION: ( -- class predicate definition )
#! Followed by a class name, then a list of union members.
CREATE
dup intern-symbol
dup predicate-word
[ dupd unit "predicate" set-word-prop ] keep
[ define-union ] [ ] ; parsing
: PREDICATE: ( -- class predicate definition )
#! Followed by a superclass name, then a class name.
scan-word
CREATE dup intern-symbol
dup rot "superclass" set-word-prop
dup predicate-word
[ define-predicate-class ] [ ] ; parsing
: TUPLE:
#! Followed by a tuple name, then slot names, then ;
scan
string-mode on
[ string-mode off define-tuple ]
f ; parsing
: M: ( -- class generic [ ] )
#! M: foo bar begins a definition of the bar generic word
#! specialized to the foo type.
scan-word scan-word [ -rot define-method ] [ ] ; parsing
: C:
#! Followed by a tuple name, then constructor code, then ;
#! Constructor code executes with the empty tuple on the
#! stack.
scan-word [ tuple-constructor ] keep
[ define-constructor ] [ ] ; parsing

View File

@ -1,12 +0,0 @@
! Copyright (C) 2005 Slava Pestov.
! See http://factor.sf.net/license.txt for BSD license.
IN: parser
USING: errors generic kernel namespaces io ;
TUPLE: parse-error file line col text ;
: parse-error ( msg -- )
file get line-number get column get line-text get
<parse-error> [ set-delegate ] keep throw ;
: with-parser ( quot -- ) [ parse-error ] recover ;

View File

@ -1,50 +1,44 @@
! Copyright (C) 2004, 2005 Slava Pestov.
! See http://factor.sf.net/license.txt for BSD license.
IN: parser
USING: errors io kernel lists math namespaces sequences words ;
USING: errors generic io kernel lists math namespaces sequences
words ;
: file-vocabs ( -- )
"scratchpad" set-in { "syntax" "scratchpad" } set-use ;
: with-parser ( quot -- ) [ parse-error ] recover ;
: parse-lines ( lines -- quot )
[
dup length [ ]
[ 1+ line-number set (parse) ] 2reduce
dup length [ ] [ 1+ line-number set (parse) ] 2reduce
reverse
] with-parser ;
: parse ( str -- code ) <string-reader> lines parse-lines ;
: eval ( "X" -- X ) parse call ;
: parse-stream ( stream name -- quot )
[ file set file-vocabs lines parse-lines ] with-scope ;
: parsing-file ( file -- ) "Loading " write print flush ;
: parse-file ( file -- quot )
dup parsing-file
[ <file-reader> ] keep parse-stream ;
dup parsing-file [ <file-reader> ] keep parse-stream ;
: run-file ( file -- )
parse-file call ;
: run-file ( file -- ) parse-file call ;
: try-run-file ( file -- )
[ [ run-file ] keep ] try drop ;
: try-run-file ( file -- ) [ [ run-file ] keep ] try drop ;
: parse-resource ( path -- quot )
#! Resources are loaded from the resource-path variable, or
#! the current directory if it is not set. Words defined in
#! resources have a definition source path starting with
#! resource:. This allows words that operate on source
#! files, like "jedit", to use a different resource path
#! at run time than was used at parse time.
dup parsing-file
[ <resource-stream> "resource:" ] keep append parse-stream ;
: run-resource ( file -- )
parse-resource call ;
: run-resource ( file -- ) parse-resource call ;
: word-file ( word -- file )
"file" word-prop dup
[ "resource:/" ?head [ resource-path ] when ] when ;
: reload ( word -- )
#! Reload the source file the word originated from.
word-file run-file ;
: reload ( word -- ) word-file run-file ;

View File

@ -0,0 +1,66 @@
USING: help io jedit parser ;
HELP: file-vocabs "( -- )"
{ $description "Installs the initial the vocabulary search path for parsing a file." } ;
HELP: parse-lines "( lines -- quot )"
{ $values { "lines" "a sequence of strings" } { "quot" "a new quotation" } }
{ $description "Parses the Factor source code which has been tokenized into lines. The vocabulary search path is taken from the current scope." }
{ $errors "Throws a parse error if the input is malformed." } ;
HELP: parse-error "( msg -- )"
{ $values { "msg" "an object" } }
{ $description "Throws a parse error encapsulating a message along with current parser state, including the file being parsed, line number, and column number." } ;
HELP: with-parser "( quot -- )"
{ $values { "quot" "a quotation" } }
{ $description "Calls a quotation, wrapping any errors thrown inside parse errors." } ;
HELP: parse "( str -- quot )"
{ $values { "str" "a string" } { "quot" "a new quotation" } }
{ $description "Parses Factor source code from a string. The current vocabulary search path is used." }
{ $errors "Throws a parse error if the input is malformed." } ;
HELP: eval "( str -- )"
{ $values { "str" "a 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." } ;
HELP: parse-stream "( stream name -- quot )"
{ $values { "stream" "an input stream" } { "name" "a file name for error reporting" } { "quot" "a new quotation" } }
{ $description "Parses Factor source code read from the stream. The initial vocabulary search path is used." }
{ $errors "Throws an I/O error if there was an error reading from the stream. Throws a parse error if the input is malformed." } ;
HELP: parse-file "( file -- quot )"
{ $values { "file" "a path name string" } { "quot" "a new quotation" } }
{ $description "Parses the Factor source code stored in a file. The initial vocabulary search path is used." }
{ $errors "Throws an I/O error if there was an error reading from the file. Throws a parse error if the input is malformed." } ;
HELP: run-file "( file -- )"
{ $values { "file" "a path name string" } }
{ $description "Parses the Factor source code stored in a file and runs it. The initial vocabulary search path is used." }
{ $errors "Throws an error if loading the file fails, there input is malformed, or if a runtime error occurs while calling the parsed quotation." } ;
HELP: try-run-file "( file -- )"
{ $values { "file" "a path name string" } }
{ $description "Forgiving variant of " { $link run-file } " which logs errors to the default stream without re-throwing them." } ;
HELP: parse-resource "( path -- quot )"
{ $values { "path" "a resource name string" } { "quot" "a new quotation" } }
{ $description "Parses a library resource." }
{ $notes "the source file name given to the parser is special for resources and begins with " { $snippet "resource:" } ". This allows words that operate on source files, like " { $link jedit } ", to use a different resource path at run time than was used at parse time." }
{ $errors "Throws an I/O error if there was an error reading the resource. Throws a parse error if the input is malformed." } ;
HELP: run-resource "( path -- )"
{ $values { "path" "a resource name string" } }
{ $description "Parses and runs a library resource." }
{ $errors "Throws an I/O error if there was an error reading the resource. Throws a parse error if the input is malformed." } ;
HELP: word-file "( word -- file )"
{ $values { "word" "a word" } { "file" "a path name string" } }
{ $description "Outputs the file name containing the most recent redefinition of the word, or " { $link f } " if the word was not defined in a file." } ;
HELP: reload "( word -- )"
{ $values { "word" "a word" } }
{ $description "Reloads the source file containing the most recent redefinition of the word." }
{ $errors "Throws an error if the word was not defined in a file, or if an error occurs while running the file." } ;

View File

@ -1,4 +1,4 @@
! Copyright (C) 2004, 2005 Slava Pestov.
! Copyright (C) 2004, 2006 Slava Pestov.
! See http://factor.sf.net/license.txt for BSD license.
! Bootstrapping trick; see doc/bootstrap.txt.
@ -7,133 +7,63 @@ USING: alien arrays errors generic hashtables kernel lists math
namespaces parser sequences strings syntax vectors
words ;
: parsing ( -- )
#! Mark the most recently defined word to execute at parse
#! time, rather than run time. The word can use 'scan' to
#! read ahead in the input stream.
word t "parsing" set-word-prop ; parsing
: inline ( -- )
#! Mark the last word to be inlined.
word t "inline" set-word-prop ; parsing
: flushable ( -- )
#! Declare that a word may be removed if the value it
#! computes is unused.
word t "flushable" set-word-prop ; parsing
: foldable ( -- )
#! Declare a word as safe for compile-time evaluation.
#! Foldable implies flushable, since we can first fold to
#! a constant then flush the constant.
word
dup t "foldable" set-word-prop
t "flushable" set-word-prop ; parsing
! Booleans
! the canonical truth value is just a symbol.
SYMBOL: t
! the canonical falsity is a special runtime object.
: f f swons ; parsing
! Lists
: [ f ; parsing
: ] reverse swons ; parsing
! Conses (whose cdr might not be a list)
: [[ f ; parsing
: ]] first2 swons swons ; parsing
! Arrays, vectors, etc
: } reverse swap call swons ; parsing
: { ( array ) [ >array ] [ ] ; parsing
: V{ ( vector ) [ >vector ] [ ] ; parsing
: H{ ( hashtable ) [ alist>hash ] [ ] ; parsing
: C{ ( complex ) [ first2 rect> ] [ ] ; parsing
: T{ ( tuple ) [ array>tuple ] [ ] ; parsing
: W{ ( wrapper ) [ first <wrapper> ] [ ] ; parsing
! Do not execute parsing word
: POSTPONE: ( -- ) scan-word swons ; parsing
! Word definitions
: :
#! Begin a word definition. Word name follows.
CREATE dup reset-generic [ define-compound ] [ ] ; parsing
: ;
#! End a word definition.
reverse swap call ; parsing
! Symbols
: SYMBOL:
#! A symbol is a word that pushes itself when executed.
CREATE dup reset-generic define-symbol ; parsing
: \
#! Word literals: \ foo
scan-word literalize swons ; parsing
! Vocabularies
: PRIMITIVE:
#! This is just for show. All flash no substance.
"You cannot define primitives in Factor" throw ; parsing
: DEFER:
#! Create a word with no definition. Used for mutually
#! recursive words.
CREATE dup reset-generic drop ; parsing
: FORGET:
#! Followed by a word name. The word is removed from its
#! vocabulary. Note that specifying an undefined word is a
#! no-op.
scan use get hash-stack [ forget ] when* ; parsing
: USE:
#! Add vocabulary to search path.
scan use+ ; parsing
: USING:
#! A list of vocabularies terminated with ;
string-mode on
[ string-mode off [ use+ ] each ]
f ; parsing
: IN:
#! Set vocabulary for new definitions.
scan set-in ; parsing
! Char literal
: CHAR: ( -- ) 0 scan next-char drop swons ; parsing
! String literal
: " parse-string swons ; parsing
: SBUF" skip-blank parse-string >sbuf swons ; parsing
! Comments
: (
#! Stack comment.
CHAR: ) ch-search until ; parsing
: !
#! EOL comment.
until-eol ; parsing
: #!
#! EOL comment.
until-eol ; parsing
! Reading integers in other bases
: (BASE) ( base -- )
#! Reads an integer in a specific base.
scan swap base> swons ;
: ( CHAR: ) ch-search until ; parsing
: ! until-eol ; parsing
: #! until-eol ; parsing
: IN: scan set-in ; parsing
: USE: scan use+ ; parsing
: USING: string-mode on [ string-mode off add-use ] f ; parsing
: (BASE) scan swap base> swons ;
: HEX: 16 (BASE) ; parsing
: DEC: 10 (BASE) ; parsing
: OCT: 8 (BASE) ; parsing
: BIN: 2 (BASE) ; parsing
SYMBOL: t
: f f swons ; parsing
: CHAR: 0 scan next-char drop swons ; parsing
: " parse-string swons ; parsing
: SBUF" skip-blank parse-string >sbuf swons ; parsing
: [ f ; parsing
: ] reverse swons ; parsing
: [[ f ; parsing
: ]] first2 swons swons ; parsing
: ; reverse swap call ; parsing
: } POSTPONE: ; swons ; parsing
: { [ >array ] [ ] ; parsing
: V{ [ >vector ] [ ] ; parsing
: H{ [ alist>hash ] [ ] ; parsing
: C{ [ first2 rect> ] [ ] ; parsing
: T{ [ array>tuple ] [ ] ; parsing
: W{ [ first <wrapper> ] [ ] ; parsing
: POSTPONE: scan-word swons ; parsing
: \ scan-word literalize swons ; parsing
: parsing word t "parsing" set-word-prop ; parsing
: inline word t "inline" set-word-prop ; parsing
: flushable ( not implemented ) ; parsing
: foldable word t "foldable" set-word-prop ; parsing
: SYMBOL: CREATE dup reset-generic define-symbol ; parsing
DEFER: PRIMITIVE: parsing
: DEFER: CREATE dup reset-generic drop ; parsing
: : CREATE dup reset-generic [ define-compound ] [ ] ; parsing
: GENERIC: CREATE dup reset-word define-generic ; parsing
: G: CREATE dup reset-word [ define-generic* ] [ ] ; parsing
: M: scan-word scan-word [ -rot define-method ] [ ] ; parsing
: UNION: ( -- class predicate definition )
CREATE dup intern-symbol dup predicate-word
[ dupd unit "predicate" set-word-prop ] keep
[ define-union ] [ ] ; parsing
: PREDICATE: ( -- class predicate definition )
scan-word CREATE dup intern-symbol
dup rot "superclass" set-word-prop dup predicate-word
[ define-predicate-class ] [ ] ; parsing
: TUPLE:
scan string-mode on [ string-mode off define-tuple ] f ;
parsing
: C:
scan-word [ tuple-constructor ] keep
[ define-constructor ] [ ] ; parsing
: FORGET: scan use get hash-stack [ forget ] when* ; parsing

View File

@ -0,0 +1,242 @@
USING: help kernel math ;
HELP: parsing ""
{ $description "Declares the most recently defined word as a parsing word." }
{ $examples "In the below example, the " { $snippet "world" } " word is never called, however its body references a parsing word which executes immediately:" { $example ": hello \"Hello parser!\" print ; parsing\n: world hello ;" "Hello parser!" } } ;
HELP: inline ""
{ $description
"Declares the most recently defined word as an inline word."
$terpri
"The compiler copies the definitions of inline words directly into the word being compiled. Combinators must be inlined in order to compile. For any other word, inlining is merely an optimization. Inlining does not affect the execution of the word in the interpreter."
} ;
HELP: flushable ""
{ $description
"Declares that the most recently defined word may be removed if the value it computes is unused."
$terpri
"Flushable words must be side-effect-free; that is, their outputs must solely depend on inputs, and they must not modify their inputs, or any other object visible outside the dynamic extent of the flushable word. A pathological case of is when a word with no outputs is declared flushable, in which case calls to this word will " { $emphasis "never" } " be compiled in."
} ;
HELP: foldable ""
{ $description
"Declares that the most recently defined word can be evaluated at compile-time. Foldable words are always " { $link flushable } "."
$terpri
"Foldable words are evaluated at compile time if all inputs are literal. Foldable words must satisfy a very strong contract:"
{ $list
{ "foldable words must satisfy the contract of " { $link flushable } " words," }
{ "foldable words must halt - for example, a word computing a series until it coverges should not be foldable, since compilation will not halt in the event the series does not converge." }
{ "both inputs and outputs of foldable words must be immutable." }
}
"The last restriction ensures that words such as " { $link clone } " do not satisfy the foldable word contract. Indeed, " { $link clone } " is " { $link flushable } ", however it may output a mutable object if its input is mutable, and so it is undesirable to evaluate it at compile-time, since doing so would give incorrect semantics for code that clones mutable objects."
$terpri
"Most operations on numbers are foldable. For example, " { $snippet "2 2 +" } " compiles to a literal 4, since " { $link + } " is declared foldable."
} ;
HELP: t "( -- t )"
{ $values { "t" "the canonical truth value" } }
{ $description "The canonical instance of " { $link general-t } ". It is just a symbol." } ;
HELP: f "( -- f )"
{ $values { "f" "the singleton false value" } }
{ $description "The " { $link f } " parsing word adds the " { $link f } " object to the parse tree, and is also the class whose sole instance is the " { $link f } " object. The " { $link f } " object is the singleton false value, the only object that is not true. The " { $link f } " object is not equal to the " { $link f } " class word, which can be pushed on the stack using word wrapper syntax:"
{ $code "f ! the singleton f object denoting falsity\n\\ f ! the f class word" } } ;
HELP: [ ""
{ $description "Marks the beginning of a literal list." }
{ $examples { $code "[ 1 2 3 ]" } }
{ $see-also POSTPONE: ] } ;
HELP: ] ""
{ $description "Marks the end of a literal list." }
{ $see-also POSTPONE: [ } ;
HELP: [[ "car cdr ]]"
{ $description "Parses two components making up a cons cell." }
{ $notes "The lists parsed with " { $link POSTPONE: [ } " and " { $link POSTPONE: ] } " are just a special case of " { $link POSTPONE: [[ } " and " { $link POSTPONE: ]] } ". The following two lines are equivalent:" { $code "[ 1 2 3 ]\n[[ 1 [[ 2 [[ 3 f ]] ]] ]]" } }
{ $see-also POSTPONE: ]] } ;
HELP: ]] ""
{ $description "Marks the end of a literal cons cell." }
{ $see-also POSTPONE: [[ } ;
HELP: } ""
{ $description "Marks the end of an array, vector, hashtable, complex number, tuple, or wrapper." }
{ $see-also POSTPONE: { POSTPONE: V{ POSTPONE: H{ POSTPONE: C{ POSTPONE: T{ POSTPONE: W{ } ;
HELP: { "elements... }"
{ $values { "elements" "a list of objects" } }
{ $description "Marks the beginning of a literal array." }
{ $examples { $code "{ 1 2 3 }" } } ;
HELP: V{ "elements... }"
{ $values { "elements" "a list of objects" } }
{ $description "Marks the beginning of a literal vector." }
{ $examples { $code "V{ 1 2 3 }" } } ;
HELP: H{ "{ key value }... }"
{ $values { "key" "an object" } { "value" "an object" } }
{ $description "Marks the beginning of a literal hashtable, given as a list of two-element arrays holding key/value pairs." }
{ $examples { $code "H{ { \"tuna\" \"fish\" } { \"jalapeno\" \"vegatable\" } }" } } ;
HELP: C{ "real imaginary }"
{ $values { "real" "a real number" } { "imaginary" "a real number" } }
{ $description "Parses a complex number given in rectangular form as a pair of real numbers." } ;
HELP: T{ "class delegate slots... }"
{ $values { "class" "a tuple class word" } { "delegate" "a delegate" } { "slots" "list of objects" } }
{ $description "Marks the beginning of a literal tuple. The class word must always be specified. If an insufficient number of values is given after the class word, the remaining slots of the tuple are set to " { $link f } ". If too many values are given, an error is thrown." } ;
HELP: POSTPONE: "word"
{ $values { "word" "a word" } }
{ $description "Reads the next word from the input string and appends the word to the parse tree, even if it is a parsing word." }
{ $examples "For an ordinary word " { $snippet "foo" } ", " { $snippet "foo" } " and " { $snippet "POSTPONE: foo" } " are equivalent; however, if " { $snippet "foo" } "\texttt{foo} is a parsing word, the former will execute it at parse time, while the latter will execute it at runtime." }
{ $notes "This word is used inside parsing words to delegate further action to another parsing word, and to refer to parsing words literally from literal arrays and such." } ;
HELP: : "word definition... ;"
{ $values { "word" "a new word to define" } { "definition" "a word definition" } }
{ $description "Defines a compound word in the current vocabulary." }
{ $examples { $code ": ask-name ( -- name )\n \"What is your name? \" write readln ;\n: greet ( name -- ) \"Greetings, \" write print ;\n: friend ( -- ) ask-name greet ;" } }
{ $see-also POSTPONE: ; } ;
HELP: ; ""
{ $description
"Marks the end of a definition."
$terpri
"Parsing words can use this word as a generic end delimiter. It has parse-time stack effect " { $snippet "( definer parsed -- )" } "; when parsed, it reverses the " { $snippet "parsed" } " quotation, and passes it as input to the " { $snippet "definer" } " quotation."
}
{ $see-also POSTPONE: : POSTPONE: G: POSTPONE: M: POSTPONE: C: POSTPONE: UNION: POSTPONE: PREDICATE: POSTPONE: USING: } ;
HELP: SYMBOL: "word"
{ $values { "word" "a new word to define" } }
{ $description "Defines a new symbol word in the current vocabulary. Symbols push themselves on the stack when executed, and are used to identify variables (see " { $link "namespaces" } ") as well as for storing crufties in word properties (see " { $link "word-props" } ")." }
{ $examples { $example "SYMBOL: foo\nfoo ." "foo" } } ;
HELP: \ "word"
{ $values { "word" "a word" } }
{ $description "Reads the next word from the input and appends a wrapper holding the word to the parse tree. When the evaluator encounters a wrapper, it pushes the wrapped word literally on the data stack." }
{ $examples { $example "\\ + ." "+" } } ;
HELP: DEFER: "word"
{ $values { "word" "a new word to define" } }
{ $description "Create a word \texttt{name} in the current vocabulary that simply raises an error when executed. Usually, the word will be replaced with a real definition later." }
{ $notes "Due to the way the parser works, words cannot be referenced before they are defined; that is, source files must order definitions in a strictly bottom-up fashion. Mutually-recursive pairs of words can be implemented by " { $emphasis "deferring" } " one of the words in the pair allowing the second word in the pair to parse, then by defining the first word." }
{ $examples { $code "DEFER: foe\n: fie ... foe ... ;\n: foe ... fie ... ;" } } ;
HELP: FORGET: "word"
{ $values { "word" "a word" } }
{ $description "Removes the word from its vocabulary, or does nothing if no such word exists. Existing definitions that reference forgotten words will continue to work, but new occurrences of the word will not parse." } ;
HELP: USE: "vocabulary"
{ $values { "vocabulary" "a vocabulary name" } }
{ $description "Adds a new vocabulary at the front of the search path. Subsequent word lookups by the parser will search this vocabulary first." }
{ $errors "Throws an error if the vocabulary does not exist." } ;
HELP: USING: "vocabularies... ;"
{ $values { "vocabularies" "a list of vocabulary names" } }
{ $description "Adds a list of vocabularies to the front of the search path, with later vocabularies taking precedence." }
{ $errors "Throws an error if one of the vocabularies does not exist." } ;
HELP: IN: "vocabulary"
{ $values { "vocabulary" "a new vocabulary name" } }
{ $description "Sets the current vocabulary where new words will be defined, creating the vocabulary first if it does not exist. After the vocabulary has been created, it can be listed in " { $link POSTPONE: USE: } " and " { $link POSTPONE: USING: } " declarations." } ;
HELP: CHAR: "token"
{ $values { "token" "a literal character or escape code" } }
{ $description "Adds the Unicode code point of the character represented by the token to the parse tree." } ;
HELP: " "string... \""
{ $values { "string" "literal and escaped characters" } }
{ $description "Reads from the input string until the next occurrence of " { $link POSTPONE: " } ", and appends the resulting string to the parse tree. String literals cannot span multiple lines. Strings containing the " { $link POSTPONE: " } " character and various other special characters can be read by inserting escape sequences." }
{ $examples { $example "\"Hello\nworld\" print" "Hello\nworld" } } ;
HELP: SBUF" "string... \""
{ $values { "string" "literal and escaped characters" } }
{ $description "Reads from the input string until the next occurrence of " { $link POSTPONE: " } ", converts the string to a string buffer, and appends it to the parse tree." }
{ $examples { $example "SBUF\" Hello world\" >string print" "Hello world" } } ;
HELP: ( "comment... )"
{ $values { "comment" "characters" } }
{ $description "Discards all input until the next occurrence of " { $snippet ")" } "." }
{ $notes "Conventionally used to denote a stack effect comment." }
{ $see-also POSTPONE: ! POSTPONE: #! } ;
HELP: ! "comment..."
{ $values { "comment" "characters" } }
{ $description "Discards all input until the end of the line." }
{ $see-also POSTPONE: ( POSTPONE: #! } ;
HELP: #! "comment..."
{ $values { "comment" "characters" } }
{ $description "Discards all input until the end of the line." }
{ $see-also POSTPONE: ( POSTPONE: ! } ;
HELP: (BASE) "( base -- )"
{ $values { "base" "an integer between 2 and 36" } }
{ $description "Reads an integer in a specific numerical base from the parser input. This word can only be called from parsing words." } ;
HELP: HEX: "integer"
{ $values { "integer" "hexadecimal digits (0-9, a-f, A-F)" } }
{ $description "Adds an integer read from a hexadecimal literal to the parse tree." }
{ $examples { $example "HEX: ff ." "255" } } ;
HELP: OCT: "integer"
{ $values { "integer" "octal digits (0-7)" } }
{ $description "Adds an integer read from an octal literal to the parse tree." }
{ $examples { $example "OCT: 31337 ." "13023" } } ;
HELP: BIN: "integer"
{ $values { "integer" "binary digits (0 and 1)" } }
{ $description "Adds an integer read from an binary literal to the parse tree." }
{ $examples { $example "BIN: 100 ." "4" } } ;
HELP: GENERIC: "word"
{ $values { "word" "a new word to define" } }
{ $description "Defines a new generic word in the current vocabulary. Initially, it contains no methods, and thus will throw an error when called." }
{ $notes
"A " { $link "method-combinations" "method combination" } " facility exists for customizing method dispatch behavior."
$terpri
"This parsing word is equivalent to the following usage of the more general " { $link POSTPONE: G: } " word:"
{ $code "G: word simple-combination ;" }
} ;
HELP: G: "word combination... ;"
{ $values { "word" "a new word to define" } { "combination" "a method combination definition with stack effect " { $snippet "( word -- quot )" } } }
{ $description "Defines a generic word using the long-form. A method combination is a quotation that is given the generic word on the stack, and outputs a quotation " { $emphasis "that becomes the definition of the word" } "." }
{ $contract "The method combination quotation is called each time the generic word has to be updated (for example, when a method is added), and thus must be side-effect free." } ;
HELP: M: "class generic definition... ;"
{ $values { "class" "a class word" } { "generic" "a generic word" } { "definition" "a method definition" } }
{ $description "Defines a method, that is, a behavior for the generic word specialized on instances of the class." } ;
HELP: UNION: "class members... ;"
{ $values { "class" "a new class word to define" } { "members" "a list of class words separated by whitespace" } }
{ $description "Defines a union class. An object is an instance of a union class if it is an instance of one of its members." }
{ $notes "Union classes are used to associate the same method with several different classes, as well as to conveniently define predicates." } ;
HELP: PREDICATE: "superclass class predicate... ;"
{ $values { "superclass" "an existing class word" } { "class" "a new class word to define" } { "predicate" "membership test with stack effect " { $snippet "( superclass -- ? )" } } }
{ $description
"Defines a predicate class deriving from " { $snippet "superclass" } "."
$terpri
"An object is an instance of a predicate class if two conditions hold:"
{ $list
"it is an instance of the predicate's superclass,"
"it satisfies the predicate"
}
"Each predicate must be defined as a subclass of some other class. This ensures that predicates inheriting from disjoint classes do not need to be exhaustively tested during method dispatch."
} ;
HELP: TUPLE: "class slots... ;"
{ $values { "class" "a new class word to define" } { "slots" "a list of slot names" } }
{ $description "Defines a new tuple class with membership predicate " { $snippet "name?" } " and constructor " { $snippet "<name>" } "."
$terpri
"Tuples are user-defined classes with instances composed of named slots. All tuple classes are subtypes of the built-in " { $link tuple } " type." } ;
HELP: C: "class definition... ;"
{ $values { "class" "a class word" } { "generic" "a generic word" } { "definition" "a constructor definition" } }
{ $description "Define a constructor word for a tuple class. The constructor definition receives a new instance of the class on the stack, with all slots initially set to " { $link f } "."
$terpri
"Constructors are named after the tuple class surrounded in angle brackets: " { $snippet "<" } " and " { $snippet ">" } "." }
{ $contract "The definition must only have one output, the new tuple itself." }
{ $notes "Each tuple class defines a default constructor that reads slot values from the stack. This parsing word redefines the default constructor." } ;

View File

@ -1,147 +0,0 @@
! Copyright (C) 2005 Slava Pestov.
! See http://factor.sf.net/license.txt for BSD license.
IN: parser
USING: errors hashtables io kernel lists math namespaces
sequences strings vectors words ;
! The parser uses a number of variables:
! line - the line being parsed
! pos - position in the line
! use - list of vocabularies
! in - vocabulary for new words
!
! When a token is scanned, it is searched for in the 'use' list
! of vocabularies. If it is a parsing word, it is executed
! immediately. Otherwise it is appended to the parse tree.
SYMBOL: use
SYMBOL: in
: check-vocab ( name -- vocab )
dup vocab
[ ] [ " is not a vocabulary name" append throw ] ?if ;
: use+ ( string -- )
#! Add a vocabulary to the search path.
check-vocab use get push ;
: set-use ( seq -- )
#! Convert to a later so we can push later.
[ check-vocab ] map >vector use set ;
: set-in ( name -- )
dup ensure-vocab dup in set use+ ;
: parsing? ( word -- ? )
dup word? [ "parsing" word-prop ] [ drop f ] if ;
SYMBOL: file
SYMBOL: line-number
SYMBOL: line-text
SYMBOL: column
: skip ( i seq quot -- n | quot: elt -- ? )
over >r find* drop dup -1 =
[ drop r> length ] [ r> drop ] if ; inline
: skip-blank ( -- )
column [ line-text get [ blank? not ] skip ] change ;
: skip-word ( n line -- n )
2dup nth CHAR: " = [ drop 1+ ] [ [ blank? ] skip ] if ;
: (scan) ( n line -- start end )
dupd 2dup length < [ skip-word ] [ drop ] if ;
: scan ( -- token )
skip-blank
column [ line-text get (scan) dup ] change
2dup = [ 2drop f ] [ line-text get subseq ] if ;
: save-location ( word -- )
#! Remember where this word was defined.
dup set-word
dup line-number get "line" set-word-prop
file get "file" set-word-prop ;
: create-in in get create dup save-location ;
: CREATE ( -- word ) scan create-in ;
! If this variable is on, the parser does not internalize words;
! it just appends strings to the parse tree as they are read.
SYMBOL: string-mode
global [ string-mode off ] bind
: scan-word ( -- obj )
scan dup [
dup ";" = not string-mode get and [
dup use get hash-stack [ ] [ string>number ] ?if
] unless
] when ;
! Used by parsing words
: ch-search ( ch -- index ) column get line-text get index* ;
: until ( index -- str ) 1+ column set ;
: until-eol ( -- )
#! This is just a hack to get "eval" to work with multiline
#! strings from jEdit with EOL comments. Normally, input to
#! the parser is already line-tokenized.
CHAR: \n ch-search dup -1 =
[ drop line-text get length ] when until ;
: escape ( ch -- esc )
H{
{ CHAR: e CHAR: \e }
{ CHAR: n CHAR: \n }
{ CHAR: r CHAR: \r }
{ CHAR: t CHAR: \t }
{ CHAR: s CHAR: \s }
{ CHAR: \s CHAR: \s }
{ CHAR: 0 CHAR: \0 }
{ CHAR: \\ CHAR: \\ }
{ CHAR: \" CHAR: \" }
} hash dup [ "Bad escape" throw ] unless ;
: next-escape ( n str -- ch n )
2dup nth CHAR: u = [
swap 1+ dup 4 + [ rot subseq hex> ] keep
] [
over 1+ >r nth escape r>
] if ;
: next-char ( n str -- ch n )
2dup nth CHAR: \\ = [
>r 1+ r> next-escape
] [
over 1+ >r nth r>
] if ;
: (parse-string) ( n str -- n )
2dup nth CHAR: " = [
drop 1+
] [
[ next-char swap , ] keep (parse-string)
] if ;
: parse-string ( -- str )
#! Read a string from the input stream, until it is
#! terminated by a ".
column [
[ line-text get (parse-string) ] "" make swap
] change ;
global [
{
"scratchpad" "syntax" "arrays" "compiler" "errors"
"generic" "hashtables" "help" "inference" "inspector"
"io" "jedit" "kernel" "listener" "lists" "math" "memory"
"namespaces" "parser" "prettyprint" "queues" "sequences"
"shells" "strings" "styles" "test" "threads" "vectors"
"walker" "words"
} set-use
"scratchpad" set-in
] bind

View File

@ -1,20 +1,127 @@
! Copyright (C) 2005 Slava Pestov.
! Copyright (C) 2005, 2006 Slava Pestov.
! See http://factor.sf.net/license.txt for BSD license.
IN: parser
USING: kernel lists namespaces sequences words ;
USING: errors generic hashtables kernel lists math namespaces
sequences strings vectors words ;
SYMBOL: use
SYMBOL: in
: check-vocab ( name -- vocab )
dup vocab
[ ] [ " is not a vocabulary name" append throw ] ?if ;
: use+ ( string -- ) check-vocab use get push ;
: add-use ( seq -- ) [ use+ ] each ;
: set-use ( seq -- ) [ check-vocab ] map >vector use set ;
: set-in ( name -- ) dup ensure-vocab dup in set use+ ;
: parsing? ( word -- ? )
dup word? [ "parsing" word-prop ] [ drop f ] if ;
SYMBOL: file
SYMBOL: line-number
SYMBOL: line-text
SYMBOL: column
TUPLE: parse-error file line col text ;
: parse-error ( msg -- )
file get line-number get column get line-text get
<parse-error> [ set-delegate ] keep throw ;
: skip ( i seq quot -- n | quot: elt -- ? )
over >r find* drop dup -1 =
[ drop r> length ] [ r> drop ] if ; inline
: skip-blank ( -- )
column [ line-text get [ blank? not ] skip ] change ;
: skip-word ( n line -- n )
2dup nth CHAR: " = [ drop 1+ ] [ [ blank? ] skip ] if ;
: (scan) ( n line -- start end )
dupd 2dup length < [ skip-word ] [ drop ] if ;
: scan ( -- token )
skip-blank
column [ line-text get (scan) dup ] change
2dup = [ 2drop f ] [ line-text get subseq ] if ;
: save-location ( word -- )
dup set-word
dup line-number get "line" set-word-prop
file get "file" set-word-prop ;
: create-in in get create dup save-location ;
: CREATE ( -- word ) scan create-in ;
SYMBOL: string-mode
: scan-word ( -- obj )
scan dup [
dup ";" = not string-mode get and [
dup use get hash-stack [ ] [ string>number ] ?if
] unless
] when ;
: parse-loop ( -- )
scan-word [
dup parsing? [ execute ] [ swons ] if parse-loop
] when* ;
: (parse) ( str -- )
line-text set 0 column set
parse-loop
line-text off column off ;
: (parse) ( str -- ) line-text set 0 column set parse-loop ;
: parse ( str -- code )
#! Parse the string into a parse tree that can be executed.
[ f swap (parse) reverse ] with-parser ;
! Parsing word utilities
: ch-search ( ch -- index ) column get line-text get index* ;
: eval ( "X" -- X ) parse call ;
: until ( index -- ) 1+ column set ;
: until-eol ( -- ) line-text get length until ;
: escape ( ch -- esc )
H{
{ CHAR: e CHAR: \e }
{ CHAR: n CHAR: \n }
{ CHAR: r CHAR: \r }
{ CHAR: t CHAR: \t }
{ CHAR: s CHAR: \s }
{ CHAR: \s CHAR: \s }
{ CHAR: 0 CHAR: \0 }
{ CHAR: \\ CHAR: \\ }
{ CHAR: \" CHAR: \" }
} hash [ "Bad escape" throw ] unless* ;
: next-escape ( n str -- n ch )
2dup nth CHAR: u =
[ >r 1+ dup 4 + tuck r> subseq hex> ]
[ over 1+ -rot nth escape ] if ;
: next-char ( n str -- n ch )
2dup nth CHAR: \\ =
[ >r 1+ r> next-escape ] [ over 1+ -rot nth ] if ;
: (parse-string) ( n str -- n )
2dup nth CHAR: " =
[ drop 1+ ] [ [ next-char , ] keep (parse-string) ] if ;
: parse-string ( -- str )
column
[ [ line-text get (parse-string) ] "" make swap ] change ;
global [
{
"scratchpad" "syntax" "arrays" "compiler" "errors"
"generic" "hashtables" "help" "inference" "inspector"
"io" "jedit" "kernel" "listener" "lists" "math" "memory"
"namespaces" "parser" "prettyprint" "queues" "sequences"
"shells" "strings" "styles" "test" "threads" "vectors"
"walker" "words"
} set-use
"scratchpad" set-in
] bind

135
library/syntax/parser.facts Normal file
View File

@ -0,0 +1,135 @@
USING: help parser sequences ;
HELP: use f
{ $description "A variable holding the current vocabulary search path as a sequence of hashtables." }
{ $see-also in use+ set-use POSTPONE: USING: POSTPONE: USE: } ;
HELP: in f
{ $description "A variable holding the current vocabulary for new definitions." }
{ $see-also use set-in POSTPONE: IN: } ;
HELP: check-vocab "( name -- vocab )"
{ $values { "name" "a string" } { "vocab" "a hashtable" } }
{ $description "Outputs a named vocabulary." }
{ $errors "Throws an error if the vocabulary does not exist." } ;
HELP: use+ "( vocab -- )"
{ $values { "vocab" "a string" } }
{ $description "Adds a new vocabulary at the front of the search path. Subsequent word lookups by the parser will search this vocabulary first." }
$parsing-note
{ $errors "Throws an error if the vocabulary does not exist." }
{ $see-also in use add-use set-use POSTPONE: USE: } ;
HELP: set-use "( seq -- )"
{ $values { "seq" "a sequence of strings" } }
{ $description "Sets the vocabulary search path. Later vocabularies take precedence." }
{ $errors "Throws an error if one of the vocabularies does not exist." }
$parsing-note
{ $see-also in use use+ add-use file-vocabs } ;
HELP: add-use "( seq -- )"
{ $values { "seq" "a sequence of strings" } }
{ $description "Adds multiple vocabularies to the search path, with later vocabularies taking precedence." }
{ $errors "Throws an error if one of the vocabularies does not exist." }
$parsing-note
{ $see-also in use use+ set-use POSTPONE: USING: } ;
HELP: set-in "( name -- )"
{ $values { "name" "a string" } }
{ $description "Sets the current vocabulary where new words will be defined, creating the vocabulary first if it does not exist." }
$parsing-note
{ $see-also in use POSTPONE: IN: } ;
HELP: parsing? "( obj -- ? )"
{ $values { "obj" "an object" } { "?" "a boolean" } }
{ $description "Tests if an object is a parsing word." }
{ $notes "Outputs " { $link f } " if the object is not a word." } ;
HELP: file f
{ $description "Variable stores the file name being parsed. This is the input parameter to " { $link parse-stream } "." } ;
HELP: line-number f
{ $description "Variable holds the line number being parsed." } ;
HELP: line-text f
{ $description "Variable holds the text of the line being parsed." } ;
HELP: column f
{ $description "Variable holds the column number being parsed." } ;
HELP: skip "( i seq quot -- n )"
{ $values { "n" "a starting index" } { "seq" "a sequence" } { "quot" "a quotation with stack effect " { $snippet "( elt -- ? )" } } }
{ $description "Variant of " { $link find* } " that outputs the length of the sequence instead of -1 if no elements satisfy the predicate." } ;
HELP: skip-blank "( -- )"
{ $description "Skips whitespace characters in the line currently being parsed." }
$parsing-note ;
HELP: skip-word "( n line -- n )"
{ $values { "n" "a non-negative integer" } { "line" "a string" } }
{ $description "Searches forward in the line for the end of a word starting at index " { $snippet "n" } ". This is a word in the Factor sense; that is, any character other than whitespace is a constituent of the word, and a quote (\") is a word by itself." }
{ $errors "Throws an error if " { $snippet "n" } " is out of bounds." } ;
HELP: (scan) "( n line -- start end )"
{ $values { "n" "a non-negative integer" } { "line" "a string" } { "start" "start offset of next word" } { "end" "end offset of next word" } }
{ $description "Scans forward for the next word in the line. If the end of the line is reached, both outputs equal the length of the line." } ;
HELP: scan "( -- token )"
{ $values { "token" "a string" } }
{ $description "Reads the next token from the line currently being parsed. This is the key word that the Factor parser is built on." }
$parsing-note ;
HELP: save-location "( word -- )"
{ $values { "word" "a word" } }
{ $description "Sets the " { $snippet "\"file\"" } " and " { $snippet "\"line\"" } " word properties to the current parser location." }
$parsing-note ;
HELP: create-in "( word -- )"
{ $values { "word" "a word name" } }
{ $description "Creates a word in the current vocabulary. Until re-defined, the word throws an error when invoked." }
$parsing-note ;
HELP: CREATE "( -- word )"
{ $values { "word" "a word" } }
{ $description "Reads the next token from the line currently being parsed, and creates a word with that name in the current vocabulary." }
{ $errors "Throws an error if the end of the line is reached." }
$parsing-note ;
HELP: string-mode f
{ $description
"Variable toggling strong mode. In string mode, the parser does not look up words, and instead just appends strings to the parse tree as they are read."
$terpri
"Since no parsing words are invoked in string mode, there is a special case that ends it; if the token " { $snippet ";" } " is read, string mode is switched off and the " { $link POSTPONE: ; } " parsing word is called."
} ;
HELP: scan-word "( -- obj )"
{ $values { "obj" "a word or a number" } }
{ $description "Reads the next token from the line currently being parsed. First tries to look up the word in the dictionary, and if the lookup fails, attempts to convert the token to a number." }
{ $errors "Throws an error if the token does not name a word, and does not parse as a number." }
$parsing-note ;
HELP: ch-search "( ch -- index )"
{ $values { "ch" "a character" } { "index" "an index in the current line" } }
{ $description "Searches the current line, starting from the parser column, for an occurrence of " { $snippet "ch" } ". If the character does not occur in the line, outputs -1." }
$parsing-note
{ $see-also POSTPONE: ( } ;
HELP: until "( index -- )"
{ $values { "index" "an index in the current line" } }
{ $description "Advances the parser column to immediately after the index." }
$parsing-note
{ $see-also POSTPONE: ( } ;
HELP: until-eol "( -- )"
{ $description "Discards all input until the end of the line." }
$parsing-note
{ $see-also POSTPONE: ! POSTPONE: #! } ;
HELP: escape "( escape -- ch )"
{ $values { "escape" "a single-character escape" } { "ch" "a character" } }
{ $description "Converts from a single-character escape code and the corresponding character." }
{ $examples { $example "CHAR: n escape CHAR: \n = ." "t" } } ;
HELP: parse-string "( -- str )"
{ $values { "str" "a new string" } }
{ $description "Parses the line until a quote (\"), interpreting escape codes along the way." } ;

View File

@ -154,9 +154,7 @@ M: block pprint-section* ( block -- )
: last-block? ( -- ? )
pprinter get pprinter-stack length 1 = ;
: block> ( -- )
#! Protect against malformed <block ... block> forms.
last-block? [ (block>) ] unless ;
: block> ( -- ) last-block? [ (block>) ] unless ;
: block; ( -- )
pprinter get pprinter-block f swap set-section-nl-after?

View File

@ -0,0 +1 @@

View File

@ -56,7 +56,9 @@ parser prettyprint sequences strings words ;
: read-packet ( -- string ) 4 read be> read ;
: eval>string ( str -- )
[ [ [ eval ] keep ] try drop ] string-out ;
[
[ [ <string-reader> "Input" parse-stream call ] keep ] try drop
] string-out ;
: wire-server ( -- )
#! Repeatedly read jEdit requests and execute them. Return