Structural Property Calculator on iPad/iPhone using Pythonista

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
calc >


Python Code
Uses sly to build this calculator.

Thanks to David Beazley. Click here to go to https://github.com/dabeaz/sly.

# -----------------------------------------------------------------------------
# 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)

Leave a Reply

Your email address will not be published. Required fields are marked *