@@ -56,98 +56,162 @@
match the behaviour of the original version(s) written in C.'''
-from__future__importprint_function
-frombuiltinsimportinput
-fromtracebackimportprint_exc
-from.parserimporttext_to_expression,ParseError,Symbol
-from.utils.stackimportstack_to_string
+frombuiltinsimportinput
+fromtracebackimportprint_exc
+from.parserimporttext_to_expression,ParseError,Symbol
+from.utils.stackimportstack_to_string
+from.utils.errorsimport(
+ NotAListError,
+ NotAnIntError,
+ StackUnderflowError,
+ )
+
+
+
[docs]defjoy(stack,expression,dictionary,viewer=None):
- '''Evaluate a Joy expression on a stack.
+ '''Evaluate a Joy expression on a stack.
- This function iterates through a sequence of terms which are either
- literals (strings, numbers, sequences of terms) or function symbols.
- Literals are put onto the stack and functions are looked up in the
- disctionary and executed.
+ This function iterates through a sequence of terms which are either
+ literals (strings, numbers, sequences of terms) or function symbols.
+ Literals are put onto the stack and functions are looked up in the
+ dictionary and executed.
- The viewer is a function that is called with the stack and expression
- on every iteration, its return value is ignored.
+ The viewer is a function that is called with the stack and expression
+ on every iteration, its return value is ignored.
- :param stack stack: The stack.
- :param stack expression: The expression to evaluate.
- :param dict dictionary: A ``dict`` mapping names to Joy functions.
- :param function viewer: Optional viewer function.
- :rtype: (stack, (), dictionary)
+ :param stack stack: The stack.
+ :param stack expression: The expression to evaluate.
+ :param dict dictionary: A ``dict`` mapping names to Joy functions.
+ :param function viewer: Optional viewer function.
+ :rtype: (stack, (), dictionary)
- '''
- whileexpression:
+ '''
+ whileexpression:
- ifviewer:viewer(stack,expression)
+ ifviewer:viewer(stack,expression)
- term,expression=expression
- ifisinstance(term,Symbol):
- term=dictionary[term]
- stack,expression,dictionary=term(stack,expression,dictionary)
- else:
- stack=term,stack
+ term,expression=expression
+ ifisinstance(term,Symbol):
+ try:
+ term=dictionary[term]
+ exceptKeyError:
+ raiseUnknownSymbolError(term)
+ stack,expression,dictionary=term(stack,expression,dictionary)
+ else:
+ stack=term,stack
- ifviewer:viewer(stack,expression)
- returnstack,expression,dictionary
[docs]defrun(text,stack,dictionary,viewer=None):
- '''
- Return the stack resulting from running the Joy code text on the stack.
+ '''
+ Return the stack resulting from running the Joy code text on the stack.
- :param str text: Joy code.
- :param stack stack: The stack.
- :param dict dictionary: A ``dict`` mapping names to Joy functions.
- :param function viewer: Optional viewer function.
- :rtype: (stack, (), dictionary)
+ :param str text: Joy code.
+ :param stack stack: The stack.
+ :param dict dictionary: A ``dict`` mapping names to Joy functions.
+ :param function viewer: Optional viewer function.
+ :rtype: (stack, (), dictionary)
- '''
- expression=text_to_expression(text)
- returnjoy(stack,expression,dictionary,viewer)
@@ -56,20 +56,25 @@
returns a dictionary of Joy functions suitable for use with the joy()function.'''
-frominspectimportgetdoc,getmembers,isfunction
-fromfunctoolsimportwraps
-fromitertoolsimportcount
+frominspectimportgetdoc,getmembers,isfunction
+fromfunctoolsimportwraps
+fromitertoolsimportcountimportoperator,math
-from.parserimporttext_to_expression,Symbol
-from.utilsimportgenerated_libraryasgenlib
-from.utils.stackimport(
- concat,
- expression_to_string,
- iter_stack,
- list_to_stack,
- pick,
- )
+from.parserimporttext_to_expression,Symbol
+from.utilsimportgenerated_libraryasgenlib
+from.utils.errorsimport(
+ NotAListError,
+ NotAnIntError,
+ StackUnderflowError,
+ )
+from.utils.stackimport(
+ concat,
+ expression_to_string,
+ iter_stack,
+ list_to_stack,
+ pick,
+ )HELP_TEMPLATE='''\
@@ -87,98 +92,97 @@
[docs]definscribe(function):
- '''A decorator to inscribe functions into the default dictionary.'''
- _dictionary[function.name]=function
- returnfunction
+ '''A decorator to inscribe functions into the default dictionary.'''
+ _dictionary[function.name]=function
+ returnfunction
[docs]definitialize():
- '''Return a dictionary of Joy functions for use with joy().'''
- return_dictionary.copy()
+ '''Return a dictionary of Joy functions for use with joy().'''
+ return_dictionary.copy()
[docs]defadd_aliases(D,A):
- '''
- Given a dict and a iterable of (name, [alias, ...]) pairs, create
- additional entries in the dict mapping each alias to the named function
- if it's in the dict. Aliases for functions not in the dict are ignored.
- '''
- forname,aliasesinA:
- try:
- F=D[name]
- exceptKeyError:
- continue
- foraliasinaliases:
- D[alias]=F
+ '''
+ Given a dict and a iterable of (name, [alias, ...]) pairs, create
+ additional entries in the dict mapping each alias to the named function
+ if it's in the dict. Aliases for functions not in the dict are ignored.
+ '''
+ forname,aliasesinA:
+ try:
+ F=D[name]
+ exceptKeyError:
+ continue
+ foraliasinaliases:
+ D[alias]=F
[docs]defFunctionWrapper(f):
- '''Set name attribute.'''
- ifnotf.__doc__:
- raiseValueError('Function %s must have doc string.'%f.__name__)
- f.name=f.__name__.rstrip('_')# Don't shadow builtins.
- returnf
+ '''Set name attribute.'''
+ ifnotf.__doc__:
+ raiseValueError('Function %s must have doc string.'%f.__name__)
+ f.name=f.__name__.rstrip('_')# Don't shadow builtins.
+ returnf
[docs]defSimpleFunctionWrapper(f):
- '''
- Wrap functions that take and return just a stack.
- '''
- @FunctionWrapper
- @wraps(f)
- definner(stack,expression,dictionary):
- returnf(stack),expression,dictionary
- returninner
+ '''
+ Wrap functions that take and return just a stack.
+ '''
+ @FunctionWrapper
+ @wraps(f)
+ definner(stack,expression,dictionary):
+ returnf(stack),expression,dictionary
+ returninner
[docs]defBinaryBuiltinWrapper(f):
- '''
- Wrap functions that take two arguments and return a single result.
- '''
- @FunctionWrapper
- @wraps(f)
- definner(stack,expression,dictionary):
- (a,(b,stack))=stack
- result=f(b,a)
- return(result,stack),expression,dictionary
- returninner
+ '''
+ Wrap functions that take two arguments and return a single result.
+ '''
+ @FunctionWrapper
+ @wraps(f)
+ definner(stack,expression,dictionary):
+ try:
+ (a,(b,stack))=stack
+ exceptValueError:
+ raiseStackUnderflowError('Not enough values on stack.')
+ if(notisinstance(a,int)
+ ornotisinstance(b,int)
+ orisinstance(a,bool)# Because bools are ints in Python.
+ orisinstance(b,bool)
+ ):
+ raiseNotAnIntError
+ result=f(b,a)
+ return(result,stack),expression,dictionary
+ returninner
[docs]defUnaryBuiltinWrapper(f):
- '''
- Wrap functions that take one argument and return a single result.
- '''
- @FunctionWrapper
- @wraps(f)
- definner(stack,expression,dictionary):
- (a,stack)=stack
- result=f(a)
- return(result,stack),expression,dictionary
- returninner
+ '''
+ Wrap functions that take one argument and return a single result.
+ '''
+ @FunctionWrapper
+ @wraps(f)
+ definner(stack,expression,dictionary):
+ (a,stack)=stack
+ result=f(a)
+ return(result,stack),expression,dictionary
+ returninner
[docs]classDefinitionWrapper(object):
- '''
- Provide implementation of defined functions, and some helper methods.
- '''
+ '''
+ Provide implementation of defined functions, and some helper methods.
+ '''
- def__init__(self,name,body_text,doc=None):
- self.name=self.__name__=name
- self.body=text_to_expression(body_text)
- self._body=tuple(iter_stack(self.body))
- self.__doc__=docorbody_text
- self._compiled=None
+ def__init__(self,name,body_text,doc=None):
+ self.name=self.__name__=name
+ self.body=text_to_expression(body_text)
+ self._body=tuple(iter_stack(self.body))
+ self.__doc__=docorbody_text
+ self._compiled=None
- def__call__(self,stack,expression,dictionary):
- ifself._compiled:
- returnself._compiled(stack,expression,dictionary)# pylint: disable=E1102
- expression=list_to_stack(self._body,expression)
- returnstack,expression,dictionary
+ def__call__(self,stack,expression,dictionary):
+ ifself._compiled:
+ returnself._compiled(stack,expression,dictionary)# pylint: disable=E1102
+ expression=list_to_stack(self._body,expression)
+ returnstack,expression,dictionary
-
[docs]@classmethod
- defparse_definition(class_,defi):
- '''
- Given some text describing a Joy function definition parse it and
- return a DefinitionWrapper.
- '''
- returnclass_(*(n.strip()fornindefi.split(None,1)))
+
[docs]@classmethod
+ defparse_definition(class_,defi):
+ '''
+ Given some text describing a Joy function definition parse it and
+ return a DefinitionWrapper.
+ '''
+ # At some point I decided that the definitions file should NOT
+ # use '==' to separate the name from the body. But somehow the
+ # xerblin\gui\default_joy_home\definitions.txt file didn't get
+ # the memo. Nor did the load_definitions() method.
+ # So I think the simplest way forward at the moment will be to
+ # edit this function to expect '=='.
-
[docs]@classmethod
- defadd_definitions(class_,defs,dictionary):
- '''
- Scan multi-line string defs for definitions and add them to the
- dictionary.
- '''
- fordefinitionin_text_to_defs(defs):
- class_.add_def(definition,dictionary)
+ name,part,body=defi.partition('==')
+ ifpart:
+ returnclass_(name.strip(),body.strip())
+ raiseValueError("No '==' in definition text %r"%(defi,))
-
[docs]@classmethod
- defadd_def(class_,definition,dictionary,fail_fails=False):
- '''
- Add the definition to the dictionary.
- '''
- F=class_.parse_definition(definition)
- dictionary[F.name]=F
+ # return class_(*(n.strip() for n in defi.split(None, 1)))
- @classmethod
- defload_definitions(class_,filename,dictionary):
- withopen(filename)asf:
- lines=[lineforlineinfif'=='inline]
- forlineinlines:
- class_.add_def(line,dictionary)
+
[docs]@classmethod
+ defadd_definitions(class_,defs,dictionary):
+ '''
+ Scan multi-line string defs for definitions and add them to the
+ dictionary.
+ '''
+ fordefinitionin_text_to_defs(defs):
+ class_.add_def(definition,dictionary)
+
+
[docs]@classmethod
+ defadd_def(class_,definition,dictionary,fail_fails=False):
+ '''
+ Add the definition to the dictionary.
+ '''
+ F=class_.parse_definition(definition)
+ dictionary[F.name]=F
[docs]@inscribe@FunctionWrapperdefinscribe_(stack,expression,dictionary):
- '''
- Create a new Joy function definition in the Joy dictionary. A
- definition is given as a string with a name followed by a double
- equal sign then one or more Joy functions, the body. for example:
+ '''
+ Create a new Joy function definition in the Joy dictionary. A
+ definition is given as a string with a name followed by a double
+ equal sign then one or more Joy functions, the body. for example:
- sqr == dup mul
+ sqr == dup mul
- If you want the definition to persist over restarts, enter it into
- the definitions.txt resource.
- '''
- definition,stack=stack
- DefinitionWrapper.add_def(definition,dictionary,fail_fails=True)
- returnstack,expression,dictionary
+ If you want the definition to persist over restarts, enter it into
+ the definitions.txt resource.
+ '''
+ definition,stack=stack
+ DefinitionWrapper.add_def(definition,dictionary,fail_fails=True)
+ returnstack,expression,dictionary
[docs]@inscribe@SimpleFunctionWrapperdefparse(stack):
- '''Parse the string on the stack to a Joy expression.'''
- text,stack=stack
- expression=text_to_expression(text)
- returnexpression,stack
+ '''Parse the string on the stack to a Joy expression.'''
+ text,stack=stack
+ expression=text_to_expression(text)
+ returnexpression,stack# @inscribe# @SimpleFunctionWrapper# def infer_(stack):
-# '''Attempt to infer the stack effect of a Joy expression.'''
-# E, stack = stack
-# effects = infer_expression(E)
-# e = list_to_stack([(fi, (fo, ())) for fi, fo in effects])
-# return e, stack
+# '''Attempt to infer the stack effect of a Joy expression.'''
+# E, stack = stack
+# effects = infer_expression(E)
+# e = list_to_stack([(fi, (fo, ())) for fi, fo in effects])
+# return e, stack
[docs]@inscribe@SimpleFunctionWrapperdefgetitem(stack):
- '''
- ::
+ '''
+ ::
- getitem == drop first
+ getitem == drop first
- Expects an integer and a quote on the stack and returns the item at the
- nth position in the quote counting from 0.
- ::
+ Expects an integer and a quote on the stack and returns the item at the
+ nth position in the quote counting from 0.
+ ::
- [a b c d] 0 getitem
- -------------------------
- a
+ [a b c d] 0 getitem
+ -------------------------
+ a
- '''
- n,(Q,stack)=stack
- returnpick(Q,n),stack
+ '''
+ n,(Q,stack)=stack
+ returnpick(Q,n),stack
[docs]@inscribe@SimpleFunctionWrapperdefdrop(stack):
- '''
- ::
+ '''
+ ::
- drop == [rest] times
+ drop == [rest] times
- Expects an integer and a quote on the stack and returns the quote with
- n items removed off the top.
- ::
+ Expects an integer and a quote on the stack and returns the quote with
+ n items removed off the top.
+ ::
- [a b c d] 2 drop
- ----------------------
- [c d]
+ [a b c d] 2 drop
+ ----------------------
+ [c d]
- '''
- n,(Q,stack)=stack
- whilen>0:
- try:
- _,Q=Q
- exceptValueError:
- raiseIndexError
- n-=1
- returnQ,stack
[docs]@inscribe@SimpleFunctionWrapperdeftake(stack):
- '''
- Expects an integer and a quote on the stack and returns the quote with
- just the top n items in reverse order (because that's easier and you can
- use reverse if needed.)
- ::
+ '''
+ Expects an integer and a quote on the stack and returns the quote with
+ just the top n items in reverse order (because that's easier and you can
+ use reverse if needed.)
+ ::
- [a b c d] 2 take
- ----------------------
- [b a]
+ [a b c d] 2 take
+ ----------------------
+ [b a]
- '''
- n,(Q,stack)=stack
- x=()
- whilen>0:
- try:
- item,Q=Q
- exceptValueError:
- raiseIndexError
- x=item,x
- n-=1
- returnx,stack
[docs]@inscribe@SimpleFunctionWrapperdefchoice(stack):
- '''
- Use a Boolean value to select one of two items.
- ::
+ '''
+ Use a Boolean value to select one of two items.
+ ::
- A B False choice
- ----------------------
- A
+ A B False choice
+ ----------------------
+ A
- A B True choice
- ---------------------
- B
+ A B True choice
+ ---------------------
+ B
- Currently Python semantics are used to evaluate the "truthiness" of the
- Boolean value (so empty string, zero, etc. are counted as false, etc.)
- '''
- (if_,(then,(else_,stack)))=stack
- returnthenifif_elseelse_,stack
+ Currently Python semantics are used to evaluate the "truthiness" of the
+ Boolean value (so empty string, zero, etc. are counted as false, etc.)
+ '''
+ (if_,(then,(else_,stack)))=stack
+ returnthenifif_elseelse_,stack
[docs]@inscribe@SimpleFunctionWrapperdefselect(stack):
- '''
- Use a Boolean value to select one of two items from a sequence.
- ::
+ '''
+ Use a Boolean value to select one of two items from a sequence.
+ ::
- [A B] False select
- ------------------------
- A
+ [A B] False select
+ ------------------------
+ A
- [A B] True select
- -----------------------
- B
+ [A B] True select
+ -----------------------
+ B
- The sequence can contain more than two items but not fewer.
- Currently Python semantics are used to evaluate the "truthiness" of the
- Boolean value (so empty string, zero, etc. are counted as false, etc.)
- '''
- (flag,(choices,stack))=stack
- (else_,(then,_))=choices
- returnthenifflagelseelse_,stack
+ The sequence can contain more than two items but not fewer.
+ Currently Python semantics are used to evaluate the "truthiness" of the
+ Boolean value (so empty string, zero, etc. are counted as false, etc.)
+ '''
+ (flag,(choices,stack))=stack
+ (else_,(then,_))=choices
+ returnthenifflagelseelse_,stack
[docs]@inscribe@SimpleFunctionWrapperdefmax_(S):
- '''Given a list find the maximum.'''
- tos,stack=S
- returnmax(iter_stack(tos)),stack
+ '''Given a list find the maximum.'''
+ tos,stack=S
+ returnmax(iter_stack(tos)),stack
[docs]@inscribe@SimpleFunctionWrapperdefmin_(S):
- '''Given a list find the minimum.'''
- tos,stack=S
- returnmin(iter_stack(tos)),stack
+ '''Given a list find the minimum.'''
+ tos,stack=S
+ returnmin(iter_stack(tos)),stack
[docs]@inscribe@SimpleFunctionWrapperdefsum_(S):
- '''
- Given a quoted sequence of numbers return the sum.
- ::
+ '''
+ Given a quoted sequence of numbers return the sum.
+ ::
- sum == 0 swap [+] step
+ sum == 0 swap [+] step
- '''
- tos,stack=S
- returnsum(iter_stack(tos)),stack
[docs]@inscribe@SimpleFunctionWrapperdefremove(S):
- '''
- Expects an item on the stack and a quote under it and removes that item
- from the the quote. The item is only removed once.
- ::
+ '''
+ Expects an item on the stack and a quote under it and removes that item
+ from the the quote. The item is only removed once.
+ ::
- [1 2 3 1] 1 remove
- ------------------------
- [2 3 1]
+ [1 2 3 1] 1 remove
+ ------------------------
+ [2 3 1]
- '''
- (tos,(second,stack))=S
- l=list(iter_stack(second))
- l.remove(tos)
- returnlist_to_stack(l),stack
[docs]@inscribe@SimpleFunctionWrapperdefunique(S):
- '''Given a list remove duplicate items.'''
- tos,stack=S
- I=list(iter_stack(tos))
- returnlist_to_stack(sorted(set(I),key=I.index)),stack
+ '''Given a list remove duplicate items.'''
+ tos,stack=S
+ I=list(iter_stack(tos))
+ returnlist_to_stack(sorted(set(I),key=I.index)),stack
[docs]@inscribe@SimpleFunctionWrapperdefsort_(S):
- '''Given a list return it sorted.'''
- tos,stack=S
- returnlist_to_stack(sorted(iter_stack(tos))),stack
+ '''Given a list return it sorted.'''
+ tos,stack=S
+ returnlist_to_stack(sorted(iter_stack(tos))),stack
[docs]@inscribe@SimpleFunctionWrapperdefdisenstacken(stack):
- '''
- The disenstacken operator expects a list on top of the stack and makes that
- the stack discarding the rest of the stack.
- '''
- returnstack[0]
+ '''
+ The disenstacken operator expects a list on top of the stack and makes that
+ the stack discarding the rest of the stack.
+ '''
+ returnstack[0]
[docs]@inscribe@SimpleFunctionWrapperdefreverse(S):
- '''
- Reverse the list on the top of the stack.
- ::
+ '''
+ Reverse the list on the top of the stack.
+ ::
- reverse == [] swap shunt
- '''
- (tos,stack)=S
- res=()
- forterminiter_stack(tos):
- res=term,res
- returnres,stack
[docs]@inscribe@SimpleFunctionWrapperdefconcat_(S):
- '''
- Concatinate the two lists on the top of the stack.
- ::
+ '''
+ Concatinate the two lists on the top of the stack.
+ ::
- [a b c] [d e f] concat
- ----------------------------
- [a b c d e f]
+ [a b c] [d e f] concat
+ ----------------------------
+ [a b c d e f]
- '''
- (tos,(second,stack))=S
- returnconcat(second,tos),stack
[docs]@inscribe@SimpleFunctionWrapperdefshunt(stack):
- '''
- Like concat but reverses the top list into the second.
- ::
+ '''
+ Like concat but reverses the top list into the second.
+ ::
- shunt == [swons] step == reverse swap concat
+ shunt == [swons] step == reverse swap concat
- [a b c] [d e f] shunt
- ---------------------------
- [f e d a b c]
+ [a b c] [d e f] shunt
+ ---------------------------
+ [f e d a b c]
- '''
- (tos,(second,stack))=stack
- whiletos:
- term,tos=tos
- second=term,second
- returnsecond,stack
[docs]@inscribe@SimpleFunctionWrapperdefzip_(S):
- '''
- Replace the two lists on the top of the stack with a list of the pairs
- from each list. The smallest list sets the length of the result list.
- '''
- (tos,(second,stack))=S
- accumulator=[
- (a,(b,()))
- fora,binzip(iter_stack(tos),iter_stack(second))
- ]
- returnlist_to_stack(accumulator),stack
+ '''
+ Replace the two lists on the top of the stack with a list of the pairs
+ from each list. The smallest list sets the length of the result list.
+ '''
+ (tos,(second,stack))=S
+ accumulator=[
+ (a,(b,()))
+ fora,binzip(iter_stack(tos),iter_stack(second))
+ ]
+ returnlist_to_stack(accumulator),stack
[docs]@inscribe@SimpleFunctionWrapperdefpm(stack):
- '''
- Plus or minus
- ::
+ '''
+ Plus or minus
+ ::
- a b pm
- -------------
- a+b a-b
+ a b pm
+ -------------
+ a+b a-b
- '''
- a,(b,stack)=stack
- p,m,=b+a,b-a
- returnm,(p,stack)
[docs]@inscribe@SimpleFunctionWrapperdefvoid(stack):
- '''True if the form on TOS is void otherwise False.'''
- form,stack=stack
- return_void(form),stack
+ '''True if the form on TOS is void otherwise False.'''
+ form,stack=stack
+ return_void(form),stackdef_void(form):
- returnany(not_void(i)foriiniter_stack(form))
+ returnany(not_void(i)foriiniter_stack(form))
@@ -749,42 +776,42 @@
[docs]@inscribe@FunctionWrapperdefwords(stack,expression,dictionary):
- '''Print all the words in alphabetical order.'''
- print(' '.join(sorted(dictionary)))
- returnstack,expression,dictionary
+ '''Print all the words in alphabetical order.'''
+ print(' '.join(sorted(dictionary)))
+ returnstack,expression,dictionary
[docs]@inscribe@FunctionWrapperdefsharing(stack,expression,dictionary):
- '''Print redistribution information.'''
- print("You may convey verbatim copies of the Program's source code as"
- ' you receive it, in any medium, provided that you conspicuously'
- ' and appropriately publish on each copy an appropriate copyright'
- ' notice; keep intact all notices stating that this License and'
- ' any non-permissive terms added in accord with section 7 apply'
- ' to the code; keep intact all notices of the absence of any'
- ' warranty; and give all recipients a copy of this License along'
- ' with the Program.'
- ' You should have received a copy of the GNU General Public License'
- ' along with Thun. If not see <http://www.gnu.org/licenses/>.')
- returnstack,expression,dictionary
+ '''Print redistribution information.'''
+ print("You may convey verbatim copies of the Program's source code as"
+ ' you receive it, in any medium, provided that you conspicuously'
+ ' and appropriately publish on each copy an appropriate copyright'
+ ' notice; keep intact all notices stating that this License and'
+ ' any non-permissive terms added in accord with section 7 apply'
+ ' to the code; keep intact all notices of the absence of any'
+ ' warranty; and give all recipients a copy of this License along'
+ ' with the Program.'
+ ' You should have received a copy of the GNU General Public License'
+ ' along with Thun. If not see <http://www.gnu.org/licenses/>.')
+ returnstack,expression,dictionary
[docs]@inscribe@FunctionWrapperdefwarranty(stack,expression,dictionary):
- '''Print warranty information.'''
- print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY'
- ' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE'
- ' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM'
- ' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR'
- ' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES'
- ' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE'
- ' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS'
- ' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE'
- ' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.')
- returnstack,expression,dictionary
+ '''Print warranty information.'''
+ print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY'
+ ' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE'
+ ' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM'
+ ' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR'
+ ' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES'
+ ' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE'
+ ' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS'
+ ' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE'
+ ' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.')
+ returnstack,expression,dictionary# def simple_manual(stack):
@@ -809,11 +836,11 @@
[docs]@inscribe@FunctionWrapperdefhelp_(S,expression,dictionary):
- '''Accepts a quoted symbol on the top of the stack and prints its docs.'''
- ((symbol,_),stack)=S
- word=dictionary[symbol]
- print(HELP_TEMPLATE%(symbol,getdoc(word),symbol))
- returnstack,expression,dictionary
+ '''Accepts a quoted symbol on the top of the stack and prints its docs.'''
+ ((symbol,_),stack)=S
+ word=dictionary[symbol]
+ print(HELP_TEMPLATE%(symbol,getdoc(word),symbol))
+ returnstack,expression,dictionary#
@@ -842,215 +869,218 @@
[docs]@inscribe@FunctionWrapperdefi(stack,expression,dictionary):
- '''
- The i combinator expects a quoted program on the stack and unpacks it
- onto the pending expression for evaluation.
- ::
+ '''
+ The i combinator expects a quoted program on the stack and unpacks it
+ onto the pending expression for evaluation.
+ ::
- [Q] i
- -----------
- Q
+ [Q] i
+ -----------
+ Q
- '''
- quote,stack=stack
- returnstack,concat(quote,expression),dictionary
[docs]@inscribe@FunctionWrapperdefb(stack,expression,dictionary):
- '''
- ::
+ '''
+ ::
- b == [i] dip i
+ b == [i] dip i
- ... [P] [Q] b == ... [P] i [Q] i
- ... [P] [Q] b == ... P Q
+ ... [P] [Q] b == ... [P] i [Q] i
+ ... [P] [Q] b == ... P Q
- '''
- q,(p,(stack))=stack
- returnstack,concat(p,concat(q,expression)),dictionary
[docs]@inscribe@FunctionWrapperdefinfra(stack,expression,dictionary):
- '''
- Accept a quoted program and a list on the stack and run the program
- with the list as its stack. Does not affect the rest of the stack.
- ::
+ '''
+ Accept a quoted program and a list on the stack and run the program
+ with the list as its stack. Does not affect the rest of the stack.
+ ::
- ... [a b c] [Q] . infra
- -----------------------------
- c b a . Q [...] swaack
+ ... [a b c] [Q] . infra
+ -----------------------------
+ c b a . Q [...] swaack
- '''
- (quote,(aggregate,stack))=stack
- returnaggregate,concat(quote,(stack,(S_swaack,expression))),dictionary
[docs]@inscribe@FunctionWrapperdefgenrec(stack,expression,dictionary):
- '''
- General Recursion Combinator.
- ::
+ '''
+ General Recursion Combinator.
+ ::
- [if] [then] [rec1] [rec2] genrec
- ---------------------------------------------------------------------
- [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
+ [if] [then] [rec1] [rec2] genrec
+ ---------------------------------------------------------------------
+ [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
- From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
- "The genrec combinator takes four program parameters in addition to
- whatever data parameters it needs. Fourth from the top is an if-part,
- followed by a then-part. If the if-part yields true, then the then-part
- is executed and the combinator terminates. The other two parameters are
- the rec1-part and the rec2-part. If the if-part yields false, the
- rec1-part is executed. Following that the four program parameters and
- the combinator are again pushed onto the stack bundled up in a quoted
- form. Then the rec2-part is executed, where it will find the bundled
- form. Typically it will then execute the bundled form, either with i or
- with app2, or some other combinator."
+ From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
+ "The genrec combinator takes four program parameters in addition to
+ whatever data parameters it needs. Fourth from the top is an if-part,
+ followed by a then-part. If the if-part yields true, then the then-part
+ is executed and the combinator terminates. The other two parameters are
+ the rec1-part and the rec2-part. If the if-part yields false, the
+ rec1-part is executed. Following that the four program parameters and
+ the combinator are again pushed onto the stack bundled up in a quoted
+ form. Then the rec2-part is executed, where it will find the bundled
+ form. Typically it will then execute the bundled form, either with i or
+ with app2, or some other combinator."
- The way to design one of these is to fix your base case [then] and the
- test [if], and then treat rec1 and rec2 as an else-part "sandwiching"
- a quotation of the whole function.
+ The way to design one of these is to fix your base case [then] and the
+ test [if], and then treat rec1 and rec2 as an else-part "sandwiching"
+ a quotation of the whole function.
- For example, given a (general recursive) function 'F':
- ::
+ For example, given a (general recursive) function 'F':
+ ::
- F == [I] [T] [R1] [R2] genrec
+ F == [I] [T] [R1] [R2] genrec
- If the [I] if-part fails you must derive R1 and R2 from:
- ::
+ If the [I] if-part fails you must derive R1 and R2 from:
+ ::
- ... R1 [F] R2
+ ... R1 [F] R2
- Just set the stack arguments in front, and figure out what R1 and R2
- have to do to apply the quoted [F] in the proper way. In effect, the
- genrec combinator turns into an ifte combinator with a quoted copy of
- the original definition in the else-part:
- ::
+ Just set the stack arguments in front, and figure out what R1 and R2
+ have to do to apply the quoted [F] in the proper way. In effect, the
+ genrec combinator turns into an ifte combinator with a quoted copy of
+ the original definition in the else-part:
+ ::
- F == [I] [T] [R1] [R2] genrec
- == [I] [T] [R1 [F] R2] ifte
+ F == [I] [T] [R1] [R2] genrec
+ == [I] [T] [R1 [F] R2] ifte
- Primitive recursive functions are those where R2 == i.
- ::
+ Primitive recursive functions are those where R2 == i.
+ ::
- P == [I] [T] [R] tailrec
- == [I] [T] [R [P] i] ifte
- == [I] [T] [R P] ifte
+ P == [I] [T] [R] tailrec
+ == [I] [T] [R [P] i] ifte
+ == [I] [T] [R P] ifte
- '''
- (rec2,(rec1,stack))=stack
- (then,(if_,_))=stack
- F=(if_,(then,(rec1,(rec2,(S_genrec,())))))
- else_=concat(rec1,(F,rec2))
- return(else_,stack),(S_ifte,expression),dictionary
[docs]@inscribe@FunctionWrapperdefmap_(S,expression,dictionary):
- '''
- Run the quoted program on TOS on the items in the list under it, push a
- new list with the results in place of the program and original list.
- '''
- # (quote, (aggregate, stack)) = S
- # results = list_to_stack([
- # joy((term, stack), quote, dictionary)[0][0]
- # for term in iter_stack(aggregate)
- # ])
- # return (results, stack), expression, dictionary
- (quote,(aggregate,stack))=S
- ifnotaggregate:
- return(aggregate,stack),expression,dictionary
- batch=()
- forterminiter_stack(aggregate):
- s=term,stack
- batch=(s,(quote,(S_infra,(S_first,batch))))
- stack=(batch,((),stack))
- returnstack,(S_infra,expression),dictionary
+ '''
+ Run the quoted program on TOS on the items in the list under it, push a
+ new list with the results in place of the program and original list.
+ '''
+ # (quote, (aggregate, stack)) = S
+ # results = list_to_stack([
+ # joy((term, stack), quote, dictionary)[0][0]
+ # for term in iter_stack(aggregate)
+ # ])
+ # return (results, stack), expression, dictionary
+ (quote,(aggregate,stack))=S
+ ifnotaggregate:
+ return(aggregate,stack),expression,dictionary
+ batch=()
+ forterminiter_stack(aggregate):
+ s=term,stack
+ batch=(s,(quote,(S_infra,(S_first,batch))))
+ stack=(batch,((),stack))
+ returnstack,(S_infra,expression),dictionary
[docs]@inscribe@FunctionWrapperdefprimrec(stack,expression,dictionary):
- '''
- From the "Overview of the language JOY":
+ '''
+ From the "Overview of the language JOY":
- > The primrec combinator expects two quoted programs in addition to a
- data parameter. For an integer data parameter it works like this: If
- the data parameter is zero, then the first quotation has to produce
- the value to be returned. If the data parameter is positive then the
- second has to combine the data parameter with the result of applying
- the function to its predecessor.::
+ > The primrec combinator expects two quoted programs in addition to a
+ data parameter. For an integer data parameter it works like this: If
+ the data parameter is zero, then the first quotation has to produce
+ the value to be returned. If the data parameter is positive then the
+ second has to combine the data parameter with the result of applying
+ the function to its predecessor.::
- 5 [1] [*] primrec
+ 5 [1] [*] primrec
- > Then primrec tests whether the top element on the stack (initially
- the 5) is equal to zero. If it is, it pops it off and executes one of
- the quotations, the [1] which leaves 1 on the stack as the result.
- Otherwise it pushes a decremented copy of the top element and
- recurses. On the way back from the recursion it uses the other
- quotation, [*], to multiply what is now a factorial on top of the
- stack by the second element on the stack.::
+ > Then primrec tests whether the top element on the stack (initially
+ the 5) is equal to zero. If it is, it pops it off and executes one of
+ the quotations, the [1] which leaves 1 on the stack as the result.
+ Otherwise it pushes a decremented copy of the top element and
+ recurses. On the way back from the recursion it uses the other
+ quotation, [*], to multiply what is now a factorial on top of the
+ stack by the second element on the stack.::
- n [Base] [Recur] primrec
+ n [Base] [Recur] primrec
- 0 [Base] [Recur] primrec
- ------------------------------
- Base
+ 0 [Base] [Recur] primrec
+ ------------------------------
+ Base
- n [Base] [Recur] primrec
- ------------------------------------------ n > 0
- n (n-1) [Base] [Recur] primrec Recur
+ n [Base] [Recur] primrec
+ ------------------------------------------ n > 0
+ n (n-1) [Base] [Recur] primrec Recur
- '''
- recur,(base,(n,stack))=stack
- ifn<=0:
- expression=concat(base,expression)
- else:
- expression=S_primrec,concat(recur,expression)
- stack=recur,(base,(n-1,(n,stack)))
- returnstack,expression,dictionary
[docs]@inscribe@FunctionWrapperdefbranch(stack,expression,dictionary):
- '''
- Use a Boolean value to select one of two quoted programs to run.
+ '''
+ Use a Boolean value to select one of two quoted programs to run.
- ::
+ ::
- branch == roll< choice i
+ branch == roll< choice i
- ::
+ ::
- False [F] [T] branch
- --------------------------
- F
+ False [F] [T] branch
+ --------------------------
+ F
- True [F] [T] branch
- -------------------------
- T
+ True [F] [T] branch
+ -------------------------
+ T
- '''
- (then,(else_,(flag,stack)))=stack
- returnstack,concat(thenifflagelseelse_,expression),dictionary
[docs]@inscribe@FunctionWrapperdefcond(stack,expression,dictionary):
- '''
- This combinator works like a case statement. It expects a single quote
- on the stack that must contain zero or more condition quotes and a
- default quote. Each condition clause should contain a quoted predicate
- followed by the function expression to run if that predicate returns
- true. If no predicates return true the default function runs.
+ '''
+ This combinator works like a case statement. It expects a single quote
+ on the stack that must contain zero or more condition quotes and a
+ default quote. Each condition clause should contain a quoted predicate
+ followed by the function expression to run if that predicate returns
+ true. If no predicates return true the default function runs.
- It works by rewriting into a chain of nested `ifte` expressions, e.g.::
+ It works by rewriting into a chain of nested `ifte` expressions, e.g.::
- [[[B0] T0] [[B1] T1] [D]] cond
- -----------------------------------------
- [B0] [T0] [[B1] [T1] [D] ifte] ifte
+ [[[B0] T0] [[B1] T1] [D]] cond
+ -----------------------------------------
+ [B0] [T0] [[B1] [T1] [D] ifte] ifte
- '''
- conditions,stack=stack
- ifconditions:
- expression=_cond(conditions,expression)
- try:
- # Attempt to preload the args to first ifte.
- (P,(T,(E,expression)))=expression
- exceptValueError:
- # If, for any reason, the argument to cond should happen to contain
- # only the default clause then this optimization will fail.
- pass
- else:
- stack=(E,(T,(P,stack)))
- returnstack,expression,dictionary
+ '''
+ conditions,stack=stack
+ ifconditions:
+ expression=_cond(conditions,expression)
+ try:
+ # Attempt to preload the args to first ifte.
+ (P,(T,(E,expression)))=expression
+ exceptValueError:
+ # If, for any reason, the argument to cond should happen to contain
+ # only the default clause then this optimization will fail.
+ pass
+ else:
+ stack=(E,(T,(P,stack)))
+ returnstack,expression,dictionarydef_cond(conditions,expression):
- (clause,rest)=conditions
- ifnotrest:# clause is [D]
- returnclause
- P,T=clause
- return(P,(T,(_cond(rest,()),(S_ifte,expression))))
+ (clause,rest)=conditions
+ ifnotrest:# clause is [D]
+ returnclause
+ P,T=clause
+ return(P,(T,(_cond(rest,()),(S_ifte,expression))))
[docs]@inscribe@FunctionWrapperdefdip(stack,expression,dictionary):
- '''
- The dip combinator expects a quoted program on the stack and below it
- some item, it hoists the item into the expression and runs the program
- on the rest of the stack.
- ::
+ '''
+ The dip combinator expects a quoted program on the stack and below it
+ some item, it hoists the item into the expression and runs the program
+ on the rest of the stack.
+ ::
- ... x [Q] dip
- -------------------
- ... Q x
+ ... x [Q] dip
+ -------------------
+ ... Q x
- '''
- (quote,(x,stack))=stack
- expression=(x,expression)
- returnstack,concat(quote,expression),dictionary
[docs]@inscribe@FunctionWrapperdefdipd(S,expression,dictionary):
- '''
- Like dip but expects two items.
- ::
+ '''
+ Like dip but expects two items.
+ ::
- ... y x [Q] dip
- ---------------------
- ... Q y x
+ ... y x [Q] dip
+ ---------------------
+ ... Q y x
- '''
- (quote,(x,(y,stack)))=S
- expression=(y,(x,expression))
- returnstack,concat(quote,expression),dictionary
[docs]@inscribe@FunctionWrapperdefdipdd(S,expression,dictionary):
- '''
- Like dip but expects three items.
- ::
+ '''
+ Like dip but expects three items.
+ ::
- ... z y x [Q] dip
- -----------------------
- ... Q z y x
+ ... z y x [Q] dip
+ -----------------------
+ ... Q z y x
- '''
- (quote,(x,(y,(z,stack))))=S
- expression=(z,(y,(x,expression)))
- returnstack,concat(quote,expression),dictionary
[docs]@inscribe@FunctionWrapperdefapp1(S,expression,dictionary):
- '''
- Given a quoted program on TOS and anything as the second stack item run
- the program and replace the two args with the first result of the
- program.
- ::
+ '''
+ Given a quoted program on TOS and anything as the second stack item run
+ the program and replace the two args with the first result of the
+ program.
+ ::
- ... x [Q] . app1
- -----------------------------------
- ... [x ...] [Q] . infra first
+ ... x [Q] . app1
+ -----------------------------------
+ ... [x ...] [Q] . infra first
- '''
- (quote,(x,stack))=S
- stack=(quote,((x,stack),stack))
- expression=(S_infra,(S_first,expression))
- returnstack,expression,dictionary
[docs]@inscribe@FunctionWrapperdefapp2(S,expression,dictionary):
- '''Like app1 with two items.
- ::
+ '''Like app1 with two items.
+ ::
- ... y x [Q] . app2
- -----------------------------------
- ... [y ...] [Q] . infra first
- [x ...] [Q] infra first
+ ... y x [Q] . app2
+ -----------------------------------
+ ... [y ...] [Q] . infra first
+ [x ...] [Q] infra first
- '''
- (quote,(x,(y,stack)))=S
- expression=(S_infra,(S_first,
- ((x,stack),(quote,(S_infra,(S_first,
- expression))))))
- stack=(quote,((y,stack),stack))
- returnstack,expression,dictionary
[docs]@inscribe@FunctionWrapperdefapp3(S,expression,dictionary):
- '''Like app1 with three items.
- ::
+ '''Like app1 with three items.
+ ::
- ... z y x [Q] . app3
- -----------------------------------
- ... [z ...] [Q] . infra first
- [y ...] [Q] infra first
- [x ...] [Q] infra first
+ ... z y x [Q] . app3
+ -----------------------------------
+ ... [z ...] [Q] . infra first
+ [y ...] [Q] infra first
+ [x ...] [Q] infra first
- '''
- (quote,(x,(y,(z,stack))))=S
- expression=(S_infra,(S_first,
- ((y,stack),(quote,(S_infra,(S_first,
- ((x,stack),(quote,(S_infra,(S_first,
- expression))))))))))
- stack=(quote,((z,stack),stack))
- returnstack,expression,dictionary
[docs]@inscribe@FunctionWrapperdefstep(S,expression,dictionary):
- '''
- Run a quoted program on each item in a sequence.
- ::
+ '''
+ Run a quoted program on each item in a sequence.
+ ::
- ... [] [Q] . step
- -----------------------
- ... .
+ ... [] [Q] . step
+ -----------------------
+ ... .
- ... [a] [Q] . step
- ------------------------
- ... a . Q
+ ... [a] [Q] . step
+ ------------------------
+ ... a . Q
- ... [a b c] [Q] . step
- ----------------------------------------
- ... a . Q [b c] [Q] step
+ ... [a b c] [Q] . step
+ ----------------------------------------
+ ... a . Q [b c] [Q] step
- The step combinator executes the quotation on each member of the list
- on top of the stack.
- '''
- (quote,(aggregate,stack))=S
- ifnotaggregate:
- returnstack,expression,dictionary
- head,tail=aggregate
- stack=quote,(head,stack)
- iftail:
- expression=tail,(quote,(S_step,expression))
- expression=S_i,expression
- returnstack,expression,dictionary
+ The step combinator executes the quotation on each member of the list
+ on top of the stack.
+ '''
+ (quote,(aggregate,stack))=S
+ ifnotaggregate:
+ returnstack,expression,dictionary
+ head,tail=aggregate
+ stack=quote,(head,stack)
+ iftail:
+ expression=tail,(quote,(S_step,expression))
+ expression=S_i,expression
+ returnstack,expression,dictionary
[docs]@inscribe@FunctionWrapperdeftimes(stack,expression,dictionary):
- '''
- times == [-- dip] cons [swap] infra [0 >] swap while pop
- ::
+ '''
+ times == [-- dip] cons [swap] infra [0 >] swap while pop
+ ::
- ... n [Q] . times
- --------------------- w/ n <= 0
- ... .
+ ... n [Q] . times
+ --------------------- w/ n <= 0
+ ... .
- ... 1 [Q] . times
- -----------------------
- ... . Q
+ ... 1 [Q] . times
+ -----------------------
+ ... . Q
- ... n [Q] . times
- ------------------------------------- w/ n > 1
- ... . Q (n - 1) [Q] times
+ ... n [Q] . times
+ ------------------------------------- w/ n > 1
+ ... . Q (n - 1) [Q] times
- '''
- # times == [-- dip] cons [swap] infra [0 >] swap while pop
- (quote,(n,stack))=stack
- ifn<=0:
- returnstack,expression,dictionary
- n-=1
- ifn:
- expression=n,(quote,(S_times,expression))
- expression=concat(quote,expression)
- returnstack,expression,dictionary
+ '''
+ # times == [-- dip] cons [swap] infra [0 >] swap while pop
+ (quote,(n,stack))=stack
+ ifn<=0:
+ returnstack,expression,dictionary
+ n-=1
+ ifn:
+ expression=n,(quote,(S_times,expression))
+ expression=concat(quote,expression)
+ returnstack,expression,dictionary# The current definition above works like this:
@@ -1362,48 +1395,57 @@
+ '''
+ try:
+ quote,stack=stack
+ exceptValueError:
+ raiseStackUnderflowError('Not enough values on stack.')
+ ifnotisinstance(quote,tuple):
+ raiseNotAListError('Loop body not a list.')
+ try:
+ (flag,stack)=stack
+ exceptValueError:
+ raiseStackUnderflowError('Not enough values on stack.')
+ ifflag:
+ expression=concat(quote,(quote,(S_loop,expression)))
+ returnstack,expression,dictionary
[docs]@inscribe@FunctionWrapperdefcmp_(stack,expression,dictionary):
- '''
- cmp takes two values and three quoted programs on the stack and runs
- one of the three depending on the results of comparing the two values:
- ::
+ '''
+ cmp takes two values and three quoted programs on the stack and runs
+ one of the three depending on the results of comparing the two values:
+ ::
- a b [G] [E] [L] cmp
- ------------------------- a > b
- G
+ a b [G] [E] [L] cmp
+ ------------------------- a > b
+ G
- a b [G] [E] [L] cmp
- ------------------------- a = b
- E
+ a b [G] [E] [L] cmp
+ ------------------------- a = b
+ E
- a b [G] [E] [L] cmp
- ------------------------- a < b
- L
- '''
- L,(E,(G,(b,(a,stack))))=stack
- expression=concat(Gifa>belseLifa<belseE,expression)
- returnstack,expression,dictionary
@@ -69,99 +69,121 @@
around square brackets.'''
-fromreimportScanner
-from.utils.stackimportlist_to_stack
+fromreimportScanner
+from.utils.stackimportlist_to_stack
-#TODO: explain the details of float lits and strings.
-FLOAT=r'-?\d+\.\d*(e(-|\+)\d+)?'
-INT=r'-?\d+'
-SYMBOL=r'[•\w!@$%^&*()_+<>?|\/;:`~,.=-]+'BRACKETS=r'\[|\]'
-STRING_DOUBLE_QUOTED=r'"(?:[^"\\]|\\.)*"'
-STRING_SINGLE_QUOTED=r"'(?:[^'\\]|\\.)*'"BLANKS=r'\s+'
+WORDS=r'[^[\]\s]+'
+
+
+token_scanner=Scanner([
+ (BRACKETS,lambda_,token:token),
+ (BLANKS,None),
+ (WORDS,lambda_,token:token),
+ ])
[docs]classSymbol(str):
- '''A string class that represents Joy function names.'''
- __repr__=str.__str__
+ '''A string class that represents Joy function names.'''
+ __repr__=str.__str__
[docs]deftext_to_expression(text):
- '''Convert a string to a Joy expression.
+ '''Convert a string to a Joy expression.
- When supplied with a string this function returns a Python datastructure
- that represents the Joy datastructure described by the text expression.
- Any unbalanced square brackets will raise a ParseError.
+ When supplied with a string this function returns a Python datastructure
+ that represents the Joy datastructure described by the text expression.
+ Any unbalanced square brackets will raise a ParseError.
- :param str text: Text to convert.
- :rtype: stack
- :raises ParseError: if the parse fails.
- '''
- return_parse(_tokenize(text))
+ :param str text: Text to convert.
+ :rtype: stack
+ :raises ParseError: if the parse fails.
+ '''
+ return_parse(_tokenize(text))
[docs]classParseError(ValueError):
- '''Raised when there is a error while parsing text.'''
+ '''Raised when there is a error while parsing text.'''
def_tokenize(text):
- '''Convert a text into a stream of tokens.
+ '''Convert a text into a stream of tokens.
- Converts function names to Symbols.
+ Converts function names to Symbols.
- Raise ParseError (with some of the failing text) if the scan fails.
- '''
- tokens,rest=_scanner.scan(text)
- ifrest:
- raiseParseError(
- 'Scan failed at position %i, %r'
- %(len(text)-len(rest),rest[:10])
- )
- returntokens
+ Raise ParseError (with some of the failing text) if the scan fails.
+ '''
+ tokens,rest=token_scanner.scan(text)
+ ifrest:
+ raiseParseError(
+ 'Scan failed at position %i, %r'
+ %(len(text)-len(rest),rest[:10])
+ )
+ returntokensdef_parse(tokens):
- '''
- Return a stack/list expression of the tokens.
- '''
- frame=[]
- stack=[]
- fortokintokens:
- iftok=='[':
- stack.append(frame)
- frame=[]
- stack[-1].append(frame)
- eliftok==']':
- try:
- frame=stack.pop()
- exceptIndexError:
- raiseParseError('Extra closing bracket.')
- frame[-1]=list_to_stack(frame[-1])
- else:
- frame.append(tok)
- ifstack:
- raiseParseError('Unclosed bracket.')
- returnlist_to_stack(frame)
-
-
-_scanner=Scanner([
- (FLOAT,lambda_,token:float(token)),
- (INT,lambda_,token:int(token)),
- (SYMBOL,lambda_,token:Symbol(token)),
- (BRACKETS,lambda_,token:token),
- (STRING_DOUBLE_QUOTED,lambda_,token:token[1:-1].replace('\\"','"')),
- (STRING_SINGLE_QUOTED,lambda_,token:token[1:-1].replace("\\'","'")),
- (BLANKS,None),
- ])
+ '''
+ Return a stack/list expression of the tokens.
+ '''
+ frame=[]
+ stack=[]
+ fortokintokens:
+ iftok=='[':
+ stack.append(frame)
+ frame=[]
+ eliftok==']':
+ v=frame
+ try:frame=stack.pop()
+ exceptIndexError:
+ raiseParseError('Extra closing bracket.')
+ frame.append(list_to_stack(v))
+ eliftok=='true':
+ frame.append(True)
+ eliftok=='false':
+ frame.append(False)
+ else:
+ try:
+ thing=int(tok)
+ exceptValueError:
+ thing=Symbol(tok)
+ frame.append(thing)
+ ifstack:
+ raiseParseError('Unclosed bracket.')
+ returnlist_to_stack(frame)
@@ -71,100 +71,121 @@
'''# (Kinda clunky and hacky. This should be swapped out in favor of much# smarter stuff.)
-from__future__importprint_function
-frombuiltinsimportobject
-fromtracebackimportprint_exc
-from.stackimportexpression_to_string,stack_to_string
-from..joyimportjoy
-from..libraryimportinscribe,FunctionWrapper
+fromtracebackimportprint_exc
+from.stackimportexpression_to_string,stack_to_string
+from..joyimportjoy
+from..libraryimportFunctionWrapper
-
[docs]@FunctionWrapperdeftrace(stack,expression,dictionary):
- '''Evaluate a Joy expression on a stack and print a trace.
+ '''Evaluate a Joy expression on a stack and print a trace.
- This function is just like the `i` combinator but it also prints a
- trace of the evaluation
+ This function is just like the `i` combinator but it also prints a
+ trace of the evaluation
- :param stack stack: The stack.
- :param stack expression: The expression to evaluate.
- :param dict dictionary: A ``dict`` mapping names to Joy functions.
- :rtype: (stack, (), dictionary)
+ :param stack stack: The stack.
+ :param stack expression: The expression to evaluate.
+ :param dict dictionary: A ``dict`` mapping names to Joy functions.
+ :rtype: (stack, (), dictionary)
- '''
- tp=TracePrinter()
- quote,stack=stack
- try:
- s,_,d=joy(stack,quote,dictionary,tp.viewer)
- except:
- tp.print_()
- print('-'*73)
- raise
- else:
- tp.print_()
- returns,expression,d
[docs]classTracePrinter(object):
- '''
- This is what does the formatting. You instantiate it and pass the ``viewer()``
- method to the :py:func:`joy.joy.joy` function, then print it to see the
- trace.
- '''
+ '''
+ This is what does the formatting. You instantiate it and pass the ``viewer()``
+ method to the :py:func:`joy.joy.joy` function, then print it to see the
+ trace.
+ '''
- def__init__(self):
- self.history=[]
+ def__init__(self):
+ self.history=[]
-
[docs]defviewer(self,stack,expression):
- '''
- Record the current stack and expression in the TracePrinter's history.
- Pass this method as the ``viewer`` argument to the :py:func:`joy.joy.joy` function.
+
[docs]defviewer(self,stack,expression):
+ '''
+ Record the current stack and expression in the TracePrinter's history.
+ Pass this method as the ``viewer`` argument to the :py:func:`joy.joy.joy` function.
- :param stack quote: A stack.
- :param stack expression: A stack.
- '''
- self.history.append((stack,expression))
+ :param stack quote: A stack.
+ :param stack expression: A stack.
+ '''
+ self.history.append((stack,expression))
[docs]defgo(self):
- '''
- Return a list of strings, one for each entry in the history, prefixed
- with enough spaces to align all the interpreter dots.
+
[docs]defgo(self):
+ '''
+ Return a list of strings, one for each entry in the history, prefixed
+ with enough spaces to align all the interpreter dots.
- This method is called internally by the ``__str__()`` method.
+ This method is called internally by the ``__str__()`` method.
- :rtype: list(str)
- '''
- max_stack_length=0
- lines=[]
- forstack,expressioninself.history:
- stack=stack_to_string(stack)
- expression=expression_to_string(expression)
- n=len(stack)
- ifn>max_stack_length:
- max_stack_length=n
- lines.append((n,'%s • %s'%(stack,expression)))
- return[# Prefix spaces to line up '•'s.
- (' '*(max_stack_length-length)+line)
- forlength,lineinlines
- ]
@@ -76,8 +76,8 @@
For example::
- def dup((head, tail)):
- return head, (head, tail)
+ def dup((head, tail)):
+ return head, (head, tail)We replace the argument "stack" by the expected structure of the stack,in this case "(head, tail)", and Python takes care of unpacking the
@@ -89,9 +89,9 @@
web page, doesn't handle tuples in the function parameters. And in Python 3, thissyntax was removed entirely. Instead you would have to write::
- def dup(stack):
- head, tail = stack
- return head, (head, tail)
+ def dup(stack):
+ head, tail = stack
+ return head, (head, tail)We have two very simple functions, one to build up a stack from a Python
@@ -103,138 +103,218 @@
.. _cons list: https://en.wikipedia.org/wiki/Cons#Lists'''
+from.errorsimportNotAListError
-frombuiltinsimportmap
[docs]deflist_to_stack(el,stack=()):
- '''Convert a Python list (or other sequence) to a Joy stack::
+ '''Convert a Python list (or other sequence) to a Joy stack::
- [1, 2, 3] -> (1, (2, (3, ())))
+ [1, 2, 3] -> (1, (2, (3, ())))
- :param list el: A Python list or other sequence (iterators and generators
- won't work because ``reverse()`` is called on ``el``.)
- :param stack stack: A stack, optional, defaults to the empty stack.
- :rtype: stack
+ :param list el: A Python list or other sequence (iterators and generators
+ won't work because ``reverse()`` is called on ``el``.)
+ :param stack stack: A stack, optional, defaults to the empty stack.
+ :rtype: stack
- '''
- foriteminreversed(el):
- stack=item,stack
- returnstack
[docs]defiter_stack(stack):
- '''Iterate through the items on the stack.
+ '''Iterate through the items on the stack.
- :param stack stack: A stack.
- :rtype: iterator
- '''
- whilestack:
- item,stack=stack
- yielditem
[docs]defstack_to_string(stack):
- '''
- Return a "pretty print" string for a stack.
+ '''
+ Return a "pretty print" string for a stack.
- The items are written right-to-left::
+ The items are written right-to-left::
- (top, (second, ...)) -> '... second top'
+ (top, (second, ...)) -> '... second top'
- :param stack stack: A stack.
- :rtype: str
- '''
- f=lambdastack:reversed(list(iter_stack(stack)))
- return_to_string(stack,f)
[docs]defexpression_to_string(expression):
- '''
- Return a "pretty print" string for a expression.
+ '''
+ Return a "pretty print" string for a expression.
- The items are written left-to-right::
+ The items are written left-to-right::
- (top, (second, ...)) -> 'top second ...'
+ (top, (second, ...)) -> 'top second ...'
- :param stack expression: A stack.
- :rtype: str
- '''
- return_to_string(expression,iter_stack)
[docs]defconcat(quote,expression):
- '''Concatinate quote onto expression.
+ '''Concatinate quote onto expression.
- In joy [1 2] [3 4] would become [1 2 3 4].
+ In joy [1 2] [3 4] would become [1 2 3 4].
- :param stack quote: A stack.
- :param stack expression: A stack.
- :raises RuntimeError: if quote is larger than sys.getrecursionlimit().
- :rtype: stack
- '''
- # This is the fastest implementation, but will trigger
- # RuntimeError: maximum recursion depth exceeded
- # on quotes longer than sys.getrecursionlimit().
+ :param stack quote: A stack.
+ :param stack expression: A stack.
+ :raises RuntimeError: if quote is larger than sys.getrecursionlimit().
+ :rtype: stack
+ '''
+ # This is the fastest implementation, but will trigger
+ # RuntimeError: maximum recursion depth exceeded
+ # on quotes longer than sys.getrecursionlimit().
- return(quote[0],concat(quote[1],expression))ifquoteelseexpression
+## return (quote[0], concat(quote[1], expression)) if quote else expression
- # Original implementation.
+ # Original implementation.## return list_to_stack(list(iter_stack(quote)), expression)
- # In-lining is slightly faster (and won't break the
- # recursion limit on long quotes.)
+ # In-lining is slightly faster (and won't break the
+ # recursion limit on long quotes.)
-## temp = []
-## while quote:
-## item, quote = quote
-## temp.append(item)
-## for item in reversed(temp):
-## expression = item, expression
-## return expression
+ temp=[]
+ whilequote:
+ ifnotisinstance(quote,tuple):
+ raiseNotAListError('Not a list.')
+ item,quote=quote
+ temp.append(item)
+ foriteminreversed(temp):
+ expression=item,expression
+ returnexpression
+
[docs]defdnd(stack,from_index,to_index):
+ '''
+ Given a stack and two indices return a rearranged stack.
+ First remove the item at from_index and then insert it at to_index,
+ the second index is relative to the stack after removal of the item
+ at from_index.
+
+ This function reuses all of the items and as much of the stack as it
+ can. It's meant to be used by remote clients to support drag-n-drop
+ rearranging of the stack from e.g. the StackListbox.
+ '''
+ assert0<=from_index
+ assert0<=to_index
+ iffrom_index==to_index:
+ returnstack
+ head,n=[],from_index
+ whileTrue:
+ item,stack=stack
+ n-=1
+ ifn<0:
+ break
+ head.append(item)
+ assertlen(head)==from_index
+ # now we have two cases:
+ diff=from_index-to_index
+ ifdiff<0:
+ # from < to
+ # so the destination index is still in the stack
+ whilediff:
+ h,stack=stack
+ head.append(h)
+ diff+=1
+ else:
+ # from > to
+ # so the destination is in the head list
+ whilediff:
+ stack=head.pop(),stack
+ diff-=1
+ stack=item,stack
+ whilehead:
+ stack=head.pop(),stack
+ returnstack
+
+
[docs]defpick(stack,n):
- '''
- Return the nth item on the stack.
+ '''
+ Return the nth item on the stack.
- :param stack stack: A stack.
- :param int n: An index into the stack.
- :raises ValueError: if ``n`` is less than zero.
- :raises IndexError: if ``n`` is equal to or greater than the length of ``stack``.
- :rtype: whatever
- '''
- ifn<0:
- raiseValueError
- whileTrue:
- try:
- item,stack=stack
- exceptValueError:
- raiseIndexError
- n-=1
- ifn<0:
- break
- returnitem
+ :param stack stack: A stack.
+ :param int n: An index into the stack.
+ :raises ValueError: if ``n`` is less than zero.
+ :raises IndexError: if ``n`` is equal to or greater than the length of ``stack``.
+ :rtype: whatever
+ '''
+ ifn<0:
+ raiseValueError
+ whileTrue:
+ try:
+ item,stack=stack
+ exceptValueError:
+ raiseIndexError
+ n-=1
+ ifn<0:
+ break
+ returnitem