@@ -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
Joy is a programming language created by Manfred von Thun that is easy to
@@ -43,17 +44,17 @@ between Thun and the originals, other than being written in Python, is
that it works by the “Continuation-Passing Style”.
I hope that this package is useful in the sense that it provides an
additional joy interpreter (the binary in the archive from La Trobe seems
to run just fine on my modern Linux machine!) But I also hope that you
can read and understand the Python code and play with the implementation
itself.
The best source (no pun intended) for learning about Joy is the
@@ -84,8 +85,8 @@ original C interpreter, Joy language source code for various functions,
and a great deal of fascinating material mostly written by Von Thun on
Joy and its deeper facets as well as how to program in it and several
interesting aspects. It’s quite a treasure trove.
This module implements an interpreter for a dialect of Joy that
attempts to stay very close to the spirit of Joy but does not precisely
match the behaviour of the original version(s) written in C.
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.
-
The viewer is a function that is called with the stack and expression
+dictionary and executed.
+
+
The viewer is a function that is called with the stack and expression
on every iteration, its return value is ignored.
-
-
-
-
-
Parameters:
-
stack (stack) – The stack.
-
expression (stack) – The expression to evaluate.
-
dictionary (dict) – A dict mapping names to Joy functions.
This is what I like to call the functions that just rearrange things on
the stack. (One thing I want to mention is that during a hypothetical
@@ -45,144 +46,144 @@ compilation phase these “stack chatter” words effectively disappear
because we can map the logical stack locations to registers that remain
static for the duration of the computation. This remains to be done but
it’s “off the shelf” technology.)
(I may have these paired up wrong. I.e. disenstacken should be
unstack and vice versa.)
-
J('1 2 3 enstacken')# Replace the stack with a quote of itself.
+
J('1 2 3 enstacken') # Replace the stack with a quote of itself.
[321]
-
J('4 5 6 [3 2 1] disenstacken')# Unpack a list onto the stack.
+
J('4 5 6 [3 2 1] disenstacken') # Unpack a list onto the stack.
456321
-
J('1 2 3 stack')# Get the stack on the stack.
+
J('1 2 3 stack') # Get the stack on the stack.
123[321]
-
J('1 2 3 [4 5 6] unstack')# Replace the stack with the list on top.
- # The items appear reversed but they are not,
- # 4 is on the top of both the list and the stack.
+
J('1 2 3 [4 5 6] unstack') # Replace the stack with the list on top.
+ # The items appear reversed but they are not,
+ # 4 is on the top of both the list and the stack.
“Swap stack” swap the list on the top of the stack for the stack, and
put the old stack on top of the new one. Think of it as a context
switch. Niether of the lists/stacks change their order.