Commits

Ahmed Youssef committed 684e66d

init

  • Participants

Comments (0)

Files changed (1)

+#       This program is free software; you can redistribute it and/or modify
+#       it under the terms of the GNU General Public License as published by
+#       the Free Software Foundation; either version 2 of the License, or
+#       (at your option) any later version.
+#       
+#       This program is distributed in the hope that it will be useful,
+#       but WITHOUT ANY WARRANTY; without even the implied warranty of
+#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#       GNU General Public License for more details.
+#       
+#       You should have received a copy of the GNU General Public License
+#       along with this program; if not, write to the Free Software
+#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#       MA 02110-1301, USA.
+#       
+
+import math 
+import decimal
+from ply import lex, yacc
+
+
+keywords = ('let', 'const', 'call')
+tokens = keywords + ('ID', 'Number', 'Const')
+literals=("=", "+", "-", "*", "/", "~", ",", "(", ")")
+
+def t_Const(tok):
+    r'[A-Z]+[0-9]?' #UPPERCASE ONLY
+    if tok.value in keywords : # is a keyword
+        tok.type=tok.value
+    return tok
+
+def t_ID(tok):
+    r'[a-z]+[0-9]?' #only lowercase.
+    if tok.value in keywords : # is a keyword
+        tok.type=tok.value
+    return tok
+
+# taken from decmial.py but without the leading sign @see gardensnake.py
+def t_Number(t):
+    r"""(\d+(\.\d*)?|\.\d+)([eE][-+]? \d+)?"""
+    t.value =  decimal.Decimal(t.value)
+    return t
+    
+def t_newline(tok):
+    r'\n+'
+    tok.lexer.lineno += tok.value.count("\n")
+
+
+def t_error(tok):
+    print "Illegal character %s"%tok.value[0]
+    tok.lexer.skip(1)
+
+t_ignore = " \t"
+
+
+lexer= lex.lex()
+#input= "let x = 5"
+
+#lexer.input(input)
+#for tok in lexer:
+    #print tok #print tok.type, " => ", tok .value, "at (%d, %d)"%(tok.lineno, tok.lexpos)
+
+
+#G:
+#stmt: varDecl|constDecl|expr
+#constDecl: const CONSTNAME ~ value
+#varDecl: LET var = NUMBER
+#funCall: CALL fn argslist
+#argslist: argslist "," Var
+#argslist: ID | Number
+#expr : expr + term
+#     | expr - term
+#     | call ID (argslist)
+#     | term
+#     | funCall
+#term : term * factor
+#     | term / factor
+#     | factor
+#factor : Number | Const | Var
+#       | ( expr )
+#       
+
+#yacc
+
+precedence = (
+    ('left','+','-'),
+    ('left','*','/'),
+    ('right','UMINUS'),
+)
+
+variables = {}
+consts = {'PI': decimal.Decimal(math.pi), 'E':decimal.Decimal(math.e)}
+
+
+def showvars():
+    return variables
+
+def showconsts():
+    return consts
+
+
+def help():
+    s="""
+    plycalc is a simple calculator implemented using ply
+    usage:
+    consts    : const CONSTNAME ~ value # CONSTNAME must be uppercase and you can't redefine it
+    variables : let varname = value     # varname [a-z][0-9]*
+    function call: call functionname([args]) 
+    
+    
+    """
+    return s
+
+funcstable={'showvars':showvars, 'showconsts':showconsts, 'help':help}
+
+def showfuncs():
+	return funcstable
+
+funcstable['showfuncs']=showfuncs
+
+for el in dir(math): #callable objects ( functions ) from math module
+    attr=getattr(math, el)
+    if callable(attr):
+        funcstable[el]=attr
+     
+def p_stmt_let(p):
+    'stmt : let ID "=" expr'
+
+    variable = p[2]
+    value    = p[4]
+    if variable in consts:
+        raise ValueError("Already declared as constant")
+    else:
+        variables[variable]=value
+
+def p_stmt_const(p):
+    'stmt : const Const "~" expr'
+    const = p[2]
+    val   = p[4]
+    if const in consts:
+        raise ValueError("Already declared as constant")
+    else: 
+        consts[const] = val
+    
+def p_stmt_expr(p):
+    'stmt : expr'
+    p[0] = p[1]
+   
+
+def p_expression_callfn(p):
+    """expr : call ID "(" argslist ")" 
+            | call ID
+    """
+    if len(p)==3:
+        func=p[2]
+        if func in funcstable:
+            f=funcstable[func]
+            p[0]=f()
+        else:
+            raise ValueError("No such a function :", func)
+    else:
+            
+        func=p[2]    
+        args=p[4]
+        cleanargs=[]
+        for arg in args:
+            if isinstance(arg, decimal.Decimal):
+                cleanargs.append(arg)
+            #resolve variables and constants 
+            elif isinstance(arg, str):
+                if arg in variables:
+                    cleanargs.append(variables[arg])
+                elif arg in consts:
+                    cleanargs.append(consts[arg])
+    
+            else: pass
+        if func in funcstable:
+            f=funcstable[func]
+            if len(cleanargs) > 0:
+                p[0]=f(*cleanargs)
+            else:
+                p[0]=f()
+        else:
+            raise ValueError("Unknown function: ", func)
+            
+            
+def p_argslist(p):
+    """argslist : argslist "," ID
+                | argslist "," Number
+                | ID
+                | Number
+                
+    """
+    args=[]
+    for el in p[1:]:
+        if isinstance(el, list):
+            args.extend(el)
+        else:
+            if el !=",":
+                args.append(el)
+    p[0]=args
+        
+
+def p_expression_uminus(p):
+    "expr : '-' expr %prec UMINUS"
+    p[0] = -p[2]
+    
+def p_expression_plus(p):
+    'expr : expr "+" term'
+    p[0] = p[1] + p[3]
+
+def p_expression_minus(p):
+    'expr : expr "-" term'
+    p[0] = p[1] + p[3]
+    
+def p_expression_term(p):
+    'expr : term'
+    p[0] = p[1]
+
+def p_factor_variable(p):
+    'factor : ID'
+    try:
+        p[0]=variables[ p[1] ]
+    except ValueError:
+        print "No such a variable"
+
+def p_factor_const(p):
+    'factor : Const'
+    try:
+        p[0]=consts[ p[1] ]
+    except ValueError:
+        print "No such a constant"
+        
+def p_term_times(p):
+    'term : term "*" factor'
+    p[0] = p[1] * p[3]
+    
+def p_term_div(p):
+    'term : term "/" factor'
+    p[0] = p[1] / p[3]
+    
+def p_term_factor(p):
+    'term : factor'
+    p[0] = p[1]
+    
+def p_factor_number(p):
+    'factor : Number'
+    p[0] = p[1]
+
+def p_factor_expr(p):
+    'factor : "(" expr ")"'
+    p[0] = p[2]
+    
+def p_error(p):
+    print "Syntax error"
+    
+p = yacc.yacc()
+while True:
+    expr = raw_input("plycalc> ")
+    try:
+        print p.parse(expr)
+        #print "vars   => ", variables
+        #print "consts => ", consts
+    except Exception as ex:
+        print ex.message