# Structural Property Calculator on iPad/iPhone using Pythonista Oct 2019

Interactive Calculator on iPad using Pythonista
Properties are mathematical functions; structural shapes are delimited with exclamation points.

Full calculator functions with named variables — and AISC structural properties!

```# General use as a numerical calculator

calc > a = 3
calc > b = 4
calc > c=(a**2+b**2)**0.5  # white space does not matter
calc > a
3.0
calc > b
4.0
calc > c
5.0
calc > 5 + (30 * 3 ** 2) - 23/46
274.5

# Use with structural properties

# W8x31
calc > ddet(!w8x31!)
8.0
calc > tf(!w8x31!)
0.435
calc > ix(!w8x31!)
110.0
calc > sy(!w8x31!)
9.27

# W24x62
calc > twdet(!w24x62!)
0.4375
calc > tw(!w24x62!)
0.43

# Single angle shapes
calc > sy(!L3x3x3/8!)
0.825
calc > sy(!L3-1/2x3-1/2x3/8!)
1.15
calc > rx(!L3-1/2x3-1/2x3/8!)
1.07
calc > t(!L3-1/2x3-1/2x3/8!)
0.375

# Double angle shapes
calc > rx(!2L3-1/2x3-1/2x3/8!)
1.07
calc > ry(!2L3-1/2x3-1/2x3/8!)
1.47

# Channel shape
calc > bfdet(!c10x15.3!)
2.625
calc > bf(!c10x15.3!)
2.6

# Area of a flange
calc > bf(!w12x40!) * tf(!w12x40!)
4.12515
calc > bf(!w12x40!) * tf(!w12x40!) * 2
8.2503

# Named variables
calc > A_flange = bf(!w12x40!) * tf(!w12x40!)
calc > A_both_flanges = 2 * A_flange
calc > A_both_flanges
8.2503
calc > A_web = a(!w12x40!) - A_both_flanges
calc > A_web
3.4497

# Assign shape to a variable name
calc > my_column = !w12x40!
calc > tf(my_column)
0.515
```
Improvement
This calculator was improved December 2019.     Structural Property Calculator — Improved
Python Code
Uses sly to build this calculator.

```# -----------------------------------------------------------------------------
# calc.py
# -----------------------------------------------------------------------------

from sly import Lexer, Parser
import AISCV1415Shapesdefsonly as V1415
import clipboard

a = V1415.getallShapes15()

class CalcLexer(Lexer):
tokens = { EXPA, EXPC, NAME, NUMBER,
PLUS, TIMES, MINUS, DIVIDE,
ASSIGN,
LPAREN, RPAREN,
SHAPE}
ignore = ' \t'

# Tokens
NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
NUMBER = r'(?=\d|\.\d)\d*(\.\d*)?'
SHAPE = r'![a-zA-Z2_\/\-][a-zA-Z0-9_\/\-\.]*!'
# ddet(!w8x31!)

# Special symbols
PLUS = r'\+'
MINUS = r'-'
# Q = r'\''
# ATFUN = r'\\$'
EXPA = r'\*\*'
EXPC = r'\^'
TIMES = r'\*'
DIVIDE = r'/'
ASSIGN = r'='
# LBRACK = r'\['
# RBRACK = r'\]'
LPAREN = r'\('
RPAREN = r'\)'

# Ignored pattern
ignore_newline = r'\n+'

# Extra action for newlines
def ignore_newline(self, t):
self.lineno += t.value.count('\n')

def error(self, t):
print('Illegal character '%s'' % t.value[0])
self.index += 1

class CalcParser(Parser):
tokens = CalcLexer.tokens

precedence = (
('left', PLUS, MINUS),
('left', TIMES, DIVIDE),
('right', EXPA, EXPC),
('right', UMINUS),
)

def __init__(self):
self.names = { }

@_('NAME ASSIGN expr')
def statement(self, p):
self.names[p.NAME] = p.expr

@_('NAME ASSIGN SHAPE')
def statement(self, p):
shape = p.SHAPE.strip('!').upper()
self.names[p.NAME] = a[shape]

@_('expr')
def statement(self, p):
print(p.expr)

@_('NAME LPAREN SHAPE RPAREN')
def expr(self, p):
shape = p.SHAPE.strip('!').upper()
try:
result = getattr(a[shape], p.NAME.upper())
except:
result = 'function failed'
return result

@_('NAME LPAREN expr RPAREN')
def expr(self, p):
#shape = p.SHAPE.strip('!').upper()
try:
result = getattr(p.expr, p.NAME.upper())
except:
result = 'function failed'
return result

@_('expr EXPA expr')
def expr(self, p):
return pow(p.expr0, p.expr1)

@_('expr EXPC expr')
def expr(self, p):
return pow(p.expr0, p.expr1)

@_('expr PLUS expr')
def expr(self, p):
return p.expr0 + p.expr1

@_('expr MINUS expr')
def expr(self, p):
return p.expr0 - p.expr1

@_('expr TIMES expr')
def expr(self, p):
return p.expr0 * p.expr1

@_('expr DIVIDE expr')
def expr(self, p):
return p.expr0 / p.expr1

@_('MINUS expr %prec UMINUS')
def expr(self, p):
return -p.expr

@_('LPAREN expr RPAREN')
def expr(self, p):
return p.expr

@_('NUMBER')
def expr(self, p):
return float(p.NUMBER)

@_('NAME')
def expr(self, p):
try:
return self.names[p.NAME]
except LookupError:
print(f'Undefined name {p.NAME!r}')
return 0

if __name__ == '__main__':
lexer = CalcLexer()
parser = CalcParser()
clipboard.set('ddet(!w8x31!)')
while True:
try:
text = input('calc > ')
except EOFError:
break
if text:
parser.parse(lexer.tokenize(text))
clipboard.set(text)

```