Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
28a1ca8a
Commit
28a1ca8a
authored
Jul 25, 2013
by
Peter Baratta
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Comment fixes
parent
af1f8c1f
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
94 additions
and
76 deletions
+94
-76
common/lib/calc/calc.py
+57
-54
common/lib/calc/preview.py
+37
-22
No files found.
common/lib/calc/calc.py
View file @
28a1ca8a
...
@@ -11,15 +11,15 @@ import numpy
...
@@ -11,15 +11,15 @@ import numpy
import
scipy.constants
import
scipy.constants
import
calcfunctions
import
calcfunctions
# Have numpy ignore errors on functions outside its domain
# Have numpy ignore errors on functions outside its domain
.
# See http://docs.scipy.org/doc/numpy/reference/generated/numpy.seterr.html
# See http://docs.scipy.org/doc/numpy/reference/generated/numpy.seterr.html
# TODO worry about thread safety/changing a global setting
numpy
.
seterr
(
all
=
'ignore'
)
# Also: 'ignore', 'warn' (default), 'raise'
numpy
.
seterr
(
all
=
'ignore'
)
# Also: 'ignore', 'warn' (default), 'raise'
from
pyparsing
import
(
Word
,
nums
,
Literal
,
from
pyparsing
import
(
ZeroOrMore
,
MatchFirst
,
Word
,
Literal
,
CaselessLiteral
,
ZeroOrMore
,
MatchFirst
,
Optional
,
Forward
,
Optional
,
Forward
,
Group
,
ParseResults
,
stringEnd
,
Suppress
,
Combine
,
alphas
,
nums
,
alphanums
CaselessLiteral
,
Group
,
ParseResults
,
)
stringEnd
,
Suppress
,
Combine
,
alphas
,
alphanums
)
DEFAULT_FUNCTIONS
=
{
DEFAULT_FUNCTIONS
=
{
'sin'
:
numpy
.
sin
,
'sin'
:
numpy
.
sin
,
...
@@ -67,11 +67,11 @@ DEFAULT_VARIABLES = {
...
@@ -67,11 +67,11 @@ DEFAULT_VARIABLES = {
}
}
# We eliminated the following extreme suffixes:
# We eliminated the following extreme suffixes:
# P (1e15), E (1e18), Z (1e21), Y (1e24),
#
P (1e15), E (1e18), Z (1e21), Y (1e24),
# f (1e-15), a (1e-18), z (1e-21), y (1e-24)
#
f (1e-15), a (1e-18), z (1e-21), y (1e-24)
# since they're rarely used, and potentially
# since they're rarely used, and potentially
confusing.
#
confusing.
They may also conflict with variables if we ever allow e.g.
# They may also conflict with variables if we ever allow e.g.
# 5R instead of 5*R
#
5R instead of 5*R
SUFFIXES
=
{
SUFFIXES
=
{
'
%
'
:
0.01
,
'k'
:
1e3
,
'M'
:
1e6
,
'G'
:
1e9
,
'T'
:
1e12
,
'
%
'
:
0.01
,
'k'
:
1e3
,
'M'
:
1e6
,
'G'
:
1e9
,
'T'
:
1e12
,
'c'
:
1e-2
,
'm'
:
1e-3
,
'u'
:
1e-6
,
'n'
:
1e-9
,
'p'
:
1e-12
'c'
:
1e-2
,
'm'
:
1e-3
,
'u'
:
1e-6
,
'n'
:
1e-9
,
'p'
:
1e-12
...
@@ -80,14 +80,14 @@ SUFFIXES = {
...
@@ -80,14 +80,14 @@ SUFFIXES = {
class
UndefinedVariable
(
Exception
):
class
UndefinedVariable
(
Exception
):
"""
"""
Indicate
the student input of a variable which was unused by the instructor
Indicate
when a student inputs a variable which was not expected.
"""
"""
pass
pass
def
lower_dict
(
input_dict
):
def
lower_dict
(
input_dict
):
"""
"""
Convert all keys in a dictionary to lowercase; keep their original values
Convert all keys in a dictionary to lowercase; keep their original values
.
Keep in mind that it is possible (but not useful?) to define different
Keep in mind that it is possible (but not useful?) to define different
variables that have the same lowercase representation. It would be hard to
variables that have the same lowercase representation. It would be hard to
...
@@ -102,7 +102,7 @@ def lower_dict(input_dict):
...
@@ -102,7 +102,7 @@ def lower_dict(input_dict):
def
super_float
(
text
):
def
super_float
(
text
):
"""
"""
Like float, but with
si extensions. 1k goes to 1000
Like float, but with
SI extensions. 1k goes to 1000.
"""
"""
if
text
[
-
1
]
in
SUFFIXES
:
if
text
[
-
1
]
in
SUFFIXES
:
return
float
(
text
[:
-
1
])
*
SUFFIXES
[
text
[
-
1
]]
return
float
(
text
[:
-
1
])
*
SUFFIXES
[
text
[
-
1
]]
...
@@ -112,10 +112,10 @@ def super_float(text):
...
@@ -112,10 +112,10 @@ def super_float(text):
def
eval_number
(
parse_result
):
def
eval_number
(
parse_result
):
"""
"""
Create a float out of its string parts
Create a float out of its string parts
.
e.g. [ '7
', '.', '13' ] -> 7.13
e.g. [ '7
.13', 'e', '3' ] -> 7130
Calls super_float above
Calls super_float above
.
"""
"""
return
super_float
(
""
.
join
(
parse_result
))
return
super_float
(
""
.
join
(
parse_result
))
...
@@ -132,28 +132,30 @@ def eval_atom(parse_result):
...
@@ -132,28 +132,30 @@ def eval_atom(parse_result):
def
eval_power
(
parse_result
):
def
eval_power
(
parse_result
):
"""
"""
Take a list of numbers and exponentiate them, right to left
Take a list of numbers and exponentiate them, right to left
.
e.g. [ 3, 2, 3 ] (which is 3^2^3 = 3^(2^3)) -> 6561
e.g. [ 2, 3, 2 ] -> 2^3^2 = 2^(3^2) -> 512
(not to be interpreted (2^3)^2 = 64)
"""
"""
# `reduce` will go from left to right; reverse the list.
parse_result
=
reversed
(
parse_result
=
reversed
(
[
k
for
k
in
parse_result
[
k
for
k
in
parse_result
if
isinstance
(
k
,
numbers
.
Number
)]
if
isinstance
(
k
,
numbers
.
Number
)]
# Ignore the '^' marks.
)
)
#
The result of an exponentiation is called a power
#
Having reversed it, raise `b` to the power of `a`.
power
=
reduce
(
lambda
a
,
b
:
b
**
a
,
parse_result
)
power
=
reduce
(
lambda
a
,
b
:
b
**
a
,
parse_result
)
return
power
return
power
def
eval_parallel
(
parse_result
):
def
eval_parallel
(
parse_result
):
"""
"""
Compute numbers according to the parallel resistors operator
Compute numbers according to the parallel resistors operator
.
BTW it is commutative. Its formula is given by
BTW it is commutative. Its formula is given by
out = 1 / (1/in1 + 1/in2 + ...)
out = 1 / (1/in1 + 1/in2 + ...)
e.g. [ 1, 2 ] -> 2/3
e.g. [ 1, 2 ] -> 2/3
Return NaN if there is a zero among the inputs
Return NaN if there is a zero among the inputs
.
"""
"""
if
len
(
parse_result
)
==
1
:
if
len
(
parse_result
)
==
1
:
return
parse_result
[
0
]
return
parse_result
[
0
]
...
@@ -166,11 +168,11 @@ def eval_parallel(parse_result):
...
@@ -166,11 +168,11 @@ def eval_parallel(parse_result):
def
eval_sum
(
parse_result
):
def
eval_sum
(
parse_result
):
"""
"""
Add the inputs
Add the inputs
, keeping in mind their sign.
[ 1, '+', 2, '-', 3 ] -> 0
[ 1, '+', 2, '-', 3 ] -> 0
Allow a leading + or -
Allow a leading + or -
.
"""
"""
total
=
0.0
total
=
0.0
current_op
=
operator
.
add
current_op
=
operator
.
add
...
@@ -186,7 +188,7 @@ def eval_sum(parse_result):
...
@@ -186,7 +188,7 @@ def eval_sum(parse_result):
def
eval_product
(
parse_result
):
def
eval_product
(
parse_result
):
"""
"""
Multiply the inputs
Multiply the inputs
.
[ 1, '*', 2, '/', 3 ] -> 0.66
[ 1, '*', 2, '/', 3 ] -> 0.66
"""
"""
...
@@ -220,27 +222,27 @@ def add_defaults(variables, functions, case_sensitive):
...
@@ -220,27 +222,27 @@ def add_defaults(variables, functions, case_sensitive):
def
evaluator
(
variables
,
functions
,
math_expr
,
case_sensitive
=
False
):
def
evaluator
(
variables
,
functions
,
math_expr
,
case_sensitive
=
False
):
"""
"""
Evaluate an expression; that is, take a string of math and return a float
Evaluate an expression; that is, take a string of math and return a float
.
-Variables are passed as a dictionary from string to value. They must be
-Variables are passed as a dictionary from string to value. They must be
python numbers
python numbers
.
-Unary functions are passed as a dictionary from string to function.
-Unary functions are passed as a dictionary from string to function.
"""
"""
# No need to go further
# No need to go further
.
if
math_expr
.
strip
()
==
""
:
if
math_expr
.
strip
()
==
""
:
return
float
(
'nan'
)
return
float
(
'nan'
)
# Parse t
ree
# Parse t
he tree.
thing
=
ParseAugmenter
(
math_expr
,
case_sensitive
)
thing
=
ParseAugmenter
(
math_expr
,
case_sensitive
)
thing
.
parse_algebra
()
thing
.
parse_algebra
()
# Get our variables together
# Get our variables together
.
all_variables
,
all_functions
=
add_defaults
(
variables
,
functions
,
case_sensitive
)
all_variables
,
all_functions
=
add_defaults
(
variables
,
functions
,
case_sensitive
)
# ...and check them
# ...and check them
thing
.
check_variables
(
all_variables
,
all_functions
)
thing
.
check_variables
(
all_variables
,
all_functions
)
# Create a recursion to evaluate the tree
# Create a recursion to evaluate the tree
.
if
case_sensitive
:
if
case_sensitive
:
casify
=
lambda
x
:
x
casify
=
lambda
x
:
x
else
:
else
:
...
@@ -262,17 +264,17 @@ def evaluator(variables, functions, math_expr, case_sensitive=False):
...
@@ -262,17 +264,17 @@ def evaluator(variables, functions, math_expr, case_sensitive=False):
class
ParseAugmenter
(
object
):
class
ParseAugmenter
(
object
):
"""
"""
Holds the data for a particular parse
Holds the data for a particular parse
.
Holds the `math_expr` and `case_sensitive` so they needn't be passed aroun
d
Retains the `math_expr` and `case_sensitive` so they needn't be passe
d
method to method.
around
method to method.
Eventually holds the parse tree and sets of variables as well.
Eventually holds the parse tree and sets of variables as well.
"""
"""
def
__init__
(
self
,
math_expr
,
case_sensitive
=
False
):
def
__init__
(
self
,
math_expr
,
case_sensitive
=
False
):
"""
"""
Create the ParseAugmenter for a given math expression string.
Create the ParseAugmenter for a given math expression string.
Have the parsing done later, when called like OBJ.parse_algebra()
Do the parsing later, when called like `OBJ.parse_algebra()`.
"""
"""
self
.
case_sensitive
=
case_sensitive
self
.
case_sensitive
=
case_sensitive
self
.
math_expr
=
math_expr
self
.
math_expr
=
math_expr
...
@@ -282,11 +284,11 @@ class ParseAugmenter(object):
...
@@ -282,11 +284,11 @@ class ParseAugmenter(object):
def
make_variable_parse_action
(
self
):
def
make_variable_parse_action
(
self
):
"""
"""
Create a wrapper to store variables as they are parsed
Create a wrapper to store variables as they are parsed
.
"""
"""
def
vpa
(
tokens
):
def
vpa
(
tokens
):
"""
"""
When a variable is recognized, store its correct form in `variables_used`
When a variable is recognized, store its correct form in `variables_used`
.
"""
"""
if
self
.
case_sensitive
:
if
self
.
case_sensitive
:
varname
=
tokens
[
0
][
0
]
varname
=
tokens
[
0
][
0
]
...
@@ -297,11 +299,11 @@ class ParseAugmenter(object):
...
@@ -297,11 +299,11 @@ class ParseAugmenter(object):
def
make_function_parse_action
(
self
):
def
make_function_parse_action
(
self
):
"""
"""
Create a wrapper to store functions as they are parsed
Create a wrapper to store functions as they are parsed
.
"""
"""
def
fpa
(
tokens
):
def
fpa
(
tokens
):
"""
"""
When a function is recognized, store its correct form in `variables_used`
When a function is recognized, store its correct form in `variables_used`
.
"""
"""
if
self
.
case_sensitive
:
if
self
.
case_sensitive
:
varname
=
tokens
[
0
][
0
]
varname
=
tokens
[
0
][
0
]
...
@@ -314,7 +316,7 @@ class ParseAugmenter(object):
...
@@ -314,7 +316,7 @@ class ParseAugmenter(object):
"""
"""
Parse an algebraic expression into a tree.
Parse an algebraic expression into a tree.
Store a `pyparsing.ParseResult` in
self.tree
with proper groupings to
Store a `pyparsing.ParseResult` in
`self.tree`
with proper groupings to
reflect parenthesis and order of operations. Leave all operators in the
reflect parenthesis and order of operations. Leave all operators in the
tree and do not parse any strings of numbers into their float versions.
tree and do not parse any strings of numbers into their float versions.
...
@@ -325,10 +327,10 @@ class ParseAugmenter(object):
...
@@ -325,10 +327,10 @@ class ParseAugmenter(object):
# 0.33 or 7 or .34 or 16.
# 0.33 or 7 or .34 or 16.
number_part
=
Word
(
nums
)
number_part
=
Word
(
nums
)
inner_number
=
(
number_part
+
Optional
(
"."
+
Optional
(
number_part
)))
|
(
"."
+
number_part
)
inner_number
=
(
number_part
+
Optional
(
"."
+
Optional
(
number_part
)))
|
(
"."
+
number_part
)
# pyparsing allows spaces between tokens--`Combine` prevents that
# pyparsing allows spaces between tokens--`Combine` prevents that
.
inner_number
=
Combine
(
inner_number
)
inner_number
=
Combine
(
inner_number
)
# SI suffixes and percent
# SI suffixes and percent
.
number_suffix
=
MatchFirst
(
Literal
(
k
)
for
k
in
SUFFIXES
.
keys
())
number_suffix
=
MatchFirst
(
Literal
(
k
)
for
k
in
SUFFIXES
.
keys
())
# 0.33k or 17
# 0.33k or 17
...
@@ -340,11 +342,11 @@ class ParseAugmenter(object):
...
@@ -340,11 +342,11 @@ class ParseAugmenter(object):
)
)
number
=
number
(
"number"
)
number
=
number
(
"number"
)
# Predefine recursive variables
# Predefine recursive variables
.
expr
=
Forward
()
expr
=
Forward
()
# Handle variables passed in. They must start with letters/underscores
# Handle variables passed in. They must start with letters/underscores
# and may contain numbers afterward
# and may contain numbers afterward
.
inner_varname
=
Word
(
alphas
+
"_"
,
alphanums
+
"_"
)
inner_varname
=
Word
(
alphas
+
"_"
,
alphanums
+
"_"
)
varname
=
Group
(
inner_varname
)(
"variable"
)
varname
=
Group
(
inner_varname
)(
"variable"
)
varname
.
setParseAction
(
self
.
make_variable_parse_action
())
varname
.
setParseAction
(
self
.
make_variable_parse_action
())
...
@@ -356,7 +358,7 @@ class ParseAugmenter(object):
...
@@ -356,7 +358,7 @@ class ParseAugmenter(object):
atom
=
number
|
function
|
varname
|
"("
+
expr
+
")"
atom
=
number
|
function
|
varname
|
"("
+
expr
+
")"
atom
=
Group
(
atom
)(
"atom"
)
atom
=
Group
(
atom
)(
"atom"
)
# Do the following in the correct order to preserve order of operation
# Do the following in the correct order to preserve order of operation
.
pow_term
=
atom
+
ZeroOrMore
(
"^"
+
atom
)
pow_term
=
atom
+
ZeroOrMore
(
"^"
+
atom
)
pow_term
=
Group
(
pow_term
)(
"power"
)
pow_term
=
Group
(
pow_term
)(
"power"
)
...
@@ -369,7 +371,7 @@ class ParseAugmenter(object):
...
@@ -369,7 +371,7 @@ class ParseAugmenter(object):
sum_term
=
Optional
(
plus_minus
)
+
prod_term
+
ZeroOrMore
(
plus_minus
+
prod_term
)
# -5 + 4 - 3
sum_term
=
Optional
(
plus_minus
)
+
prod_term
+
ZeroOrMore
(
plus_minus
+
prod_term
)
# -5 + 4 - 3
sum_term
=
Group
(
sum_term
)(
"sum"
)
sum_term
=
Group
(
sum_term
)(
"sum"
)
# Finish the recursion
# Finish the recursion
.
expr
<<
sum_term
# pylint: disable=W0104
expr
<<
sum_term
# pylint: disable=W0104
self
.
tree
=
(
expr
+
stringEnd
)
.
parseString
(
self
.
math_expr
)[
0
]
self
.
tree
=
(
expr
+
stringEnd
)
.
parseString
(
self
.
math_expr
)[
0
]
...
@@ -379,12 +381,12 @@ class ParseAugmenter(object):
...
@@ -379,12 +381,12 @@ class ParseAugmenter(object):
`handle_actions` is a dictionary of node names (e.g. 'product', 'sum',
`handle_actions` is a dictionary of node names (e.g. 'product', 'sum',
etc&) to functions. These functions are of the following form:
etc&) to functions. These functions are of the following form:
-input: a list of processed child nodes. If it includes any terminal
-input: a list of processed child nodes. If it includes any terminal
nodes in the list, they will be given as their processed forms also.
nodes in the list, they will be given as their processed forms also.
-output: whatever to be passed to the level higher, and what to
-output: whatever to be passed to the level higher, and what to
return for the final node.
return for the final node.
`handle_terminal` is a function that takes in a token and returns a
`handle_terminal` is a function that takes in a token and returns a
processed form. Leaving it as `None` just
keeps it as the identity
.
processed form. Leaving it as `None` just
leaves them as strings
.
"""
"""
def
handle_node
(
node
):
def
handle_node
(
node
):
"""
"""
...
@@ -394,6 +396,7 @@ class ParseAugmenter(object):
...
@@ -394,6 +396,7 @@ class ParseAugmenter(object):
feed it the output of `handle_node` for each child node.
feed it the output of `handle_node` for each child node.
"""
"""
if
not
isinstance
(
node
,
ParseResults
):
if
not
isinstance
(
node
,
ParseResults
):
# Then it is a terminal node.
if
handle_terminal
is
None
:
if
handle_terminal
is
None
:
return
node
return
node
else
:
else
:
...
@@ -407,7 +410,7 @@ class ParseAugmenter(object):
...
@@ -407,7 +410,7 @@ class ParseAugmenter(object):
handled_kids
=
[
handle_node
(
k
)
for
k
in
node
]
handled_kids
=
[
handle_node
(
k
)
for
k
in
node
]
return
action
(
handled_kids
)
return
action
(
handled_kids
)
# Find the value of the entire tree
# Find the value of the entire tree
.
return
handle_node
(
self
.
tree
)
return
handle_node
(
self
.
tree
)
def
check_variables
(
self
,
valid_variables
,
valid_functions
):
def
check_variables
(
self
,
valid_variables
,
valid_functions
):
...
@@ -416,7 +419,7 @@ class ParseAugmenter(object):
...
@@ -416,7 +419,7 @@ class ParseAugmenter(object):
Otherwise, raise an UndefinedVariable containing all bad variables.
Otherwise, raise an UndefinedVariable containing all bad variables.
"""
"""
# Test that `used_vars` is a subset of `all_vars`; also do functions
# Test that `used_vars` is a subset of `all_vars`; also do functions
.
if
not
(
self
.
variables_used
.
issubset
(
valid_variables
)
and
if
not
(
self
.
variables_used
.
issubset
(
valid_variables
)
and
self
.
functions_used
.
issubset
(
valid_functions
)):
self
.
functions_used
.
issubset
(
valid_functions
)):
bad_vars
=
self
.
variables_used
.
difference
(
valid_variables
)
bad_vars
=
self
.
variables_used
.
difference
(
valid_variables
)
...
...
common/lib/calc/preview.py
View file @
28a1ca8a
...
@@ -5,7 +5,7 @@ That is, given a math string, parse it and render each branch of the result,
...
@@ -5,7 +5,7 @@ That is, given a math string, parse it and render each branch of the result,
always returning valid latex.
always returning valid latex.
Because intermediate values of the render contain more data than simply the
Because intermediate values of the render contain more data than simply the
string of latex, store it in a custom class `LatexRendered`
string of latex, store it in a custom class `LatexRendered`
.
"""
"""
from
calc
import
ParseAugmenter
,
add_defaults
,
SUFFIXES
from
calc
import
ParseAugmenter
,
add_defaults
,
SUFFIXES
...
@@ -16,26 +16,26 @@ class LatexRendered(object):
...
@@ -16,26 +16,26 @@ class LatexRendered(object):
Data structure to hold a typeset representation of some math.
Data structure to hold a typeset representation of some math.
Fields:
Fields:
-`latex` is a generated, valid latex string (as if it were standalone)
-`latex` is a generated, valid latex string (as if it were standalone).
-`sans_parens` is usually the same as `latex` except without the outermost
-`sans_parens` is usually the same as `latex` except without the outermost
parens (if applicable)
parens (if applicable).
-`tall` is a boolean representing if the latex has any elements extending
-`tall` is a boolean representing if the latex has any elements extending
above or below a normal height, specifically things of the form 'a^b' and
above or below a normal height, specifically things of the form 'a^b' and
'
\f
rac{a}{b}'. This affects the height of wrapping parenthesis.
'
\f
rac{a}{b}'. This affects the height of wrapping parenthesis.
"""
"""
def
__init__
(
self
,
latex
,
parens
=
None
,
tall
=
False
):
def
__init__
(
self
,
latex
,
parens
=
None
,
tall
=
False
):
"""
"""
Instantiate with the latex representing the math
Instantiate with the latex representing the math
.
Optionally include parenthesis to wrap around it and the height.
Optionally include parenthesis to wrap around it and the height.
`parens` must be one of '(', '[' or '{'
`parens` must be one of '(', '[' or '{'
.
`tall` is a boolean (see note above)
`tall` is a boolean (see note above)
.
"""
"""
self
.
latex
=
latex
self
.
latex
=
latex
self
.
sans_parens
=
latex
self
.
sans_parens
=
latex
self
.
tall
=
tall
self
.
tall
=
tall
#
generate parens and overwrite self.latex
#
Generate parens and overwrite `self.latex`.
if
parens
is
not
None
:
if
parens
is
not
None
:
left_parens
=
parens
left_parens
=
parens
if
left_parens
==
'{'
:
if
left_parens
==
'{'
:
...
@@ -107,6 +107,7 @@ def function_closure(functions, casify):
...
@@ -107,6 +107,7 @@ def function_closure(functions, casify):
if
casify
(
fname
)
not
in
functions
:
if
casify
(
fname
)
not
in
functions
:
pass
pass
# Wrap the input of the function with parens or braces.
inner
=
children
[
1
]
.
latex
inner
=
children
[
1
]
.
latex
if
fname
==
"sqrt"
:
if
fname
==
"sqrt"
:
inner
=
u"{{{expr}}}"
.
format
(
expr
=
inner
)
inner
=
u"{{{expr}}}"
.
format
(
expr
=
inner
)
...
@@ -116,6 +117,7 @@ def function_closure(functions, casify):
...
@@ -116,6 +117,7 @@ def function_closure(functions, casify):
else
:
else
:
inner
=
u"({expr})"
.
format
(
expr
=
inner
)
inner
=
u"({expr})"
.
format
(
expr
=
inner
)
# Correctly format the name of the function.
if
fname
==
"sqrt"
:
if
fname
==
"sqrt"
:
fname
=
ur"\sqrt"
fname
=
ur"\sqrt"
elif
fname
==
"log10"
:
elif
fname
==
"log10"
:
...
@@ -125,8 +127,10 @@ def function_closure(functions, casify):
...
@@ -125,8 +127,10 @@ def function_closure(functions, casify):
else
:
else
:
fname
=
ur"\text{{{fname}}}"
.
format
(
fname
=
fname
)
fname
=
ur"\text{{{fname}}}"
.
format
(
fname
=
fname
)
# Put it together.
latex
=
fname
+
inner
latex
=
fname
+
inner
return
LatexRendered
(
latex
,
tall
=
children
[
1
]
.
tall
)
return
LatexRendered
(
latex
,
tall
=
children
[
1
]
.
tall
)
# Return the function within the closure.
return
render_function
return
render_function
...
@@ -134,7 +138,8 @@ def render_power(children):
...
@@ -134,7 +138,8 @@ def render_power(children):
"""
"""
Combine powers so that the latex is wrapped in curly braces correctly.
Combine powers so that the latex is wrapped in curly braces correctly.
If you have 'a^(b+c)' don't include that last set of parens ('a^{b+c}').
Also, if you have 'a^(b+c)' don't include that last set of parens:
'a^{b+c}' is correct, whereas 'a^{(b+c)}' is extraneous.
"""
"""
if
len
(
children
)
==
1
:
if
len
(
children
)
==
1
:
return
children
[
0
]
return
children
[
0
]
...
@@ -149,7 +154,7 @@ def render_power(children):
...
@@ -149,7 +154,7 @@ def render_power(children):
def
render_parallel
(
children
):
def
render_parallel
(
children
):
"""
"""
Simply
combine element
s with a double vertical line.
Simply
join the child node
s with a double vertical line.
"""
"""
children_latex
=
[
k
.
latex
for
k
in
children
if
k
.
latex
!=
"||"
]
children_latex
=
[
k
.
latex
for
k
in
children
if
k
.
latex
!=
"||"
]
latex
=
r"\|"
.
join
(
children_latex
)
latex
=
r"\|"
.
join
(
children_latex
)
...
@@ -161,7 +166,7 @@ def render_frac(numerator, denominator):
...
@@ -161,7 +166,7 @@ def render_frac(numerator, denominator):
r"""
r"""
Given a list of elements in the numerator and denominator, return a '\frac'
Given a list of elements in the numerator and denominator, return a '\frac'
Avoid parens if the
re is only thing in that part
Avoid parens if the
y are unnecessary (i.e. the only thing in that part).
"""
"""
if
len
(
numerator
)
==
1
:
if
len
(
numerator
)
==
1
:
num_latex
=
numerator
[
0
]
.
sans_parens
num_latex
=
numerator
[
0
]
.
sans_parens
...
@@ -181,9 +186,15 @@ def render_product(children):
...
@@ -181,9 +186,15 @@ def render_product(children):
r"""
r"""
Format products and division nicely.
Format products and division nicely.
That is, group bunches of adjacent, equal operators. For every time it
Group bunches of adjacent, equal operators. Every time it switches from
switches from numerator to denominator, call `render_frac`. Join these
denominator to the next numerator, call `render_frac`. Join these groupings
groupings by '\cdot's.
together with '\cdot's, ending on a numerator if needed.
Examples: (`children` is formed indirectly by the string on the left)
'a*b' -> 'a\cdot b'
'a/b' -> '\frac{a}{b}'
'a*b/c/d' -> '\frac{a\cdot b}{c\cdot d}'
'a/b*c/d*e' -> '\frac{a}{b}\cdot \frac{c}{d}\cdot e'
"""
"""
position
=
"numerator"
# or denominator
position
=
"numerator"
# or denominator
fraction_mode_ever
=
False
fraction_mode_ever
=
False
...
@@ -194,29 +205,33 @@ def render_product(children):
...
@@ -194,29 +205,33 @@ def render_product(children):
for
kid
in
children
:
for
kid
in
children
:
if
position
==
"numerator"
:
if
position
==
"numerator"
:
if
kid
.
latex
==
"*"
:
if
kid
.
latex
==
"*"
:
pass
pass
# Don't explicitly add the '\cdot' yet.
elif
kid
.
latex
==
"/"
:
elif
kid
.
latex
==
"/"
:
# Switch to denominator mode.
fraction_mode_ever
=
True
fraction_mode_ever
=
True
position
=
"denominator"
position
=
"denominator"
else
:
else
:
numerator
.
append
(
kid
)
numerator
.
append
(
kid
)
else
:
else
:
if
kid
.
latex
==
"*"
:
if
kid
.
latex
==
"*"
:
# render the current fraction and add it to the latex
# Switch back to numerator mode.
# First, render the current fraction and add it to the latex.
latex
+=
render_frac
(
numerator
,
denominator
)
+
r"\cdot "
latex
+=
render_frac
(
numerator
,
denominator
)
+
r"\cdot "
#
r
eset back to beginning state
#
R
eset back to beginning state
position
=
"numerator"
position
=
"numerator"
numerator
=
[]
numerator
=
[]
denominator
=
[]
denominator
=
[]
elif
kid
.
latex
==
"/"
:
elif
kid
.
latex
==
"/"
:
pass
pass
# Don't explicitly add a '\frac' yet.
else
:
else
:
denominator
.
append
(
kid
)
denominator
.
append
(
kid
)
# Add the fraction/numerator that we ended on.
if
position
==
"denominator"
:
if
position
==
"denominator"
:
latex
+=
render_frac
(
numerator
,
denominator
)
latex
+=
render_frac
(
numerator
,
denominator
)
else
:
else
:
# We ended on a numerator--act like normal multiplication.
num_latex
=
r"\cdot "
.
join
(
k
.
latex
for
k
in
numerator
)
num_latex
=
r"\cdot "
.
join
(
k
.
latex
for
k
in
numerator
)
latex
+=
num_latex
latex
+=
num_latex
...
@@ -226,7 +241,7 @@ def render_product(children):
...
@@ -226,7 +241,7 @@ def render_product(children):
def
render_sum
(
children
):
def
render_sum
(
children
):
"""
"""
Co
mbine elements, including their
operators.
Co
ncatenate elements, including the
operators.
"""
"""
children_latex
=
[
k
.
latex
for
k
in
children
]
children_latex
=
[
k
.
latex
for
k
in
children
]
latex
=
""
.
join
(
children_latex
)
latex
=
""
.
join
(
children_latex
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment