コード例 #1
0
ファイル: semantic.py プロジェクト: JoseCapela/scurry
def flatten(n):
    if not Node.is_node(n): return [n]
    if not n.type.endswith("_list"):
        return [n]
    else:
        l = []
        for i in n.args:
            l.extend(flatten(i))
        return l
コード例 #2
0
ファイル: semantic.py プロジェクト: JoseCapela/scurry
def check(node):
    if not Node.is_node(node):
        # Se for uma lista de nós (é iterable mas não é string)
        if hasattr(node,"__iter__") and type(node) != type(""):
            for i in node:
                check(i)
        else:
            return node
    else:
        current_node = node

        # PROGRAM
        if node.type in ["program"]:
            context_stack.push(Context())
            check(node.args)
            context_stack.pop()

        # PROC-FUNC-AND-VAR-LIST, STATEMENT-LIST
        elif node.type in ['proc_func_and_var_list','statement_list']:
            return check(node.args)

        # IDENTIFIER
        elif node.type in ['identifier']:
            return node.args[0]

        # VAR, GLOBAL-VAR
        elif node.type in ['global_var', 'var']: # Criação de uma nova variável
            # A regra é: variable_declaration -> type identifier
            # Em vez de existir um elif para type, obtemos logo aqui o seu valor
            # Daí: node.args[0] é type; node.args[0].args[0] é símbolo terminal TYPE
            var_type = node.args[0].args[0]
            var_name = node.args[1].args[0] # O mesmo para o nome da variável
            set_var(var_type, var_name)

        # FUNCTION
        elif node.type in ['function','procedure']:
            # As regras da gramática:
            # function_declaration -> function_heading COLON statement_sequence END
            # function_heading : type identifier
            #                  | type identifier LPAREN parameter_list RPAREN
            #     Com p[0] = Node("function_head",ln(p),p[2],p[4],p[1])
            head = node.args[0]
            name = head.args[0].args[0] # Nome é o primeiro filho
            check_if_function(name)

            if node.type == 'procedure' and len(head.args) == 1: # Apenas o nome
                args = [] # Nos procedimentos sem parâms é só nome que é posto na AST
            elif node.type == 'function' and len(head.args) == 2: # Nome e tipo
                args = [] # Nas funções sem parâmetros, é só nome e tipo de retorno
            else:
                args = flatten(head.args[1])
                args = map(lambda x: (x.args[0].args[0],x.args[1].args[0]), args)

            if node.type == 'procedure':
                rettype = 'void'
            else:
                rettype = head.args[-1].args[0]

            functions[name] = (rettype,args)

            context_stack.push(Context(name))
            for i in args:
                set_var(i[1], i[0])
            set_var(rettype, 'result') # Variável que permite armazenar o retorno
            context_stack.top().var_count['result'] += 1 # Não mostrar warning
            check(node.args[1])
            context_stack.pop()

        # FUNCTION-CALL, FUNCTION-CALL-INLINE
        elif node.type in ["function_call","function_call_inline"]:
            fname = node.args[0].args[0]
            if fname not in functions:
                raise_exception(node, "Function %s is not defined" % fname)
            if len(node.args) > 1:
                args = get_params(node.args[1])
            else:
                args = []
            rettype,vargs = functions[fname]

            if len(args) != len(vargs):
                raise_exception(node, "Function %s is expecting %d parameters and " \
                    "got %d" % (fname, len(vargs), len(args)))
            else:
                for i in range(len(vargs)):
                    if vargs[i][1] != args[i]:
                        raise_exception(node, "Parameter #%d passed to function %s should " \
                            "be of type %s and not %s" % (i+1,fname,vargs[i][1],args[i]))
            return rettype

        # ASSIGN
        elif node.type == "assign": # Atribuição (ex: a = 1; )
            varn = check(node.args[0]) # Nome da variável

            # if is_function_name(varn): # Como no pascal
            #     vartype = functions[varn][0]
            if varn == 'result': # Se a variável corresponde ao valor de retorno
                # func_name = context_stack.top().name # Nome da função em que estamos

                # Isto não é suficiente porque podemos estar dentro de um ciclo FOR
                # por exemplo, e então temos de descer na pilha de contextos
                for c in context_stack:
                    if c.name is not None:
                        func_name = c.name # Nome da função em que estamos
                        break
                if func_name is None:
                    raise_exception(node, "Cannot use result value")
                vartype = functions[func_name][0] # Identificar o tipo de retorno
            else:
                if not has_var(varn):
                    raise_exception(node, "Variable %s not declared" % varn)
                vartype = get_var(varn)
            assgntype = check(node.args[1])

            if vartype != assgntype:
                raise_exception(node, "Variable %s is of type %s and does not " \
                    "support %s" % (varn, vartype, assgntype))

        # VAR-ASSIGN
        elif node.type == "var_assign": # Declaração e atribuição (ex: int a = 1; )
            var_type = node.args[0].args[0]
            var_name = node.args[1].args[0]
            set_var(var_type, var_name)
            assgntype = check(node.args[2])

            if var_type != assgntype:
                raise_exception(node, "Variable %s is of type %s and does not " \
                    "support %s" %(var_name, var_type, assgntype))

        # AND, OR
        elif node.type == 'and_or':
            op = node.args[0].args[0]
            for i in range(1,2):
                a = check(node.args[i])
                if a != "bool":
                    raise_exception(node, "%s requires a boolean. Got %s instead." % (op,a))

        # OP
        elif node.type == "op":
            op = node.args[0].args[0]
            vt1 = check(node.args[1])
            vt2 = check(node.args[2])

            if vt1 != vt2:
                raise_exception(node, "Arguments of operation '%s' must be of the " \
                    "same type. Got %s and %s." % (op,vt1,vt2))

            if op in ['mod', '%']:
                if vt1 != 'int':
                    raise_exception(node, "Operation %s requires integers." % op)

            if op == '/':
                if vt1 != 'real' and vt1 != 'int':
                    raise_exception(node, "Operation %s requires numbers." % op)

            if op in ['==','<=','>=','>','<','<>']:
                return 'bool'
            else:
                return vt1

        # IF, WHILE, DO, UNLESS
        elif node.type in ['if','while','do','unless']:
            # Análise da condição lógica
            if node.type in ['do','unless']: # Nos 'do' e 'unless' aparece no final
                t = check(node.args[1])
            else:
                t = check(node.args[0])
            # Se o resultado da análise da expressão não é booleano
            if t != 'bool':
                raise_exception(node, "%s condition requires a boolean. " \
                    "Got %s instead." % (node.type,t))
            # Análise do corpo
            if node.type in ['do','unless']:
                check(node.args[0])
            else:
                check(node.args[1])
            # Se tem mais que dois argumentos, terceiro é um else (ou else-if)
            if len(node.args) > 2:
                check(node.args[2])

        # FOR
        elif node.type == 'for':
            context_stack.push(Context())
            v = node.args[0].args[0].args[0] # Variável contador
            st = node.args[0].args[1].args[0].type # Atribuição inicial ao contador
            fv = node.args[2].args[0].type # Valor final para o contador

            if st != 'int':
                raise_exception(node, 'For requires an integer as a starting value')
            elif fv not in ['int', 'identifier']: # Valor final para o contador
                raise_exception(node, 'For requires an integer as a final value')
            elif fv == 'identifier' and check(node.args[2]) != 'int':
                # Valor final para o contador é uma variável inteira
                raise_exception(node, 'For requires an integer as a final value')
            else: # Cria a variável contador neste contexto
                set_var('int', v)
                context_stack.top().var_count[v] += 1 # Evitar warning de var não usada

            if len(node.args) == 5: # Existe uma variável de incremento
                iv = node.args[4].args[0].type
                if iv != 'int':
                    raise_exception(node, 'For requires an integer as an incremental value')

            check(node.args[3]) # Analisar o conteúdo do ciclo for
            context_stack.pop()

        # NOT
        elif node.type == 'not': # Negação de um 'element'. Ao nível da semântica
            # basta que verifiquemos se está tudo correcto com o 'element'. A negação
            # é feita no builder.py
            return check(node.args[0])

        # ELEMENT
        elif node.type == "element":
            if node.args[0].type == 'identifier':
                return get_var(node.args[0].args[0])
            elif node.args[0].type == 'function_call_inline':
                return check(node.args[0])
            else:
                if node.args[0].type in types: # Se é um tipo de dados
                    return node.args[0].type
                else:
                    return check(node.args[0])

        else: # Se for encontrado um outro tipo de nó inesperado (só acontece se o
            # rules.py foi modificado)
            print "semantic missing:", node.type
コード例 #3
0
ファイル: rules.py プロジェクト: yitaodong/pascal-compiler
def p_procedure_or_function(t):
    """procedure_or_function : proc_or_func_declaration SEMICOLON procedure_or_function
		| """

    if len(t) == 4:
        t[0] = Node('function_list', t[1], t[3])
コード例 #4
0
ファイル: rules.py プロジェクト: skeptycal/pascal-in-python
def p_for_statement(t):
	"""for_statement : FOR assignment_statement TO expression DO statement
	| FOR assignment_statement DOWNTO expression DO statement
	"""
	t[0] = Node('for',t[2],t[3],t[4],t[6])
コード例 #5
0
ファイル: rules.py プロジェクト: skeptycal/pascal-in-python
def p_and_or(t):
	""" and_or : AND
	| OR """
	t[0] = Node('and_or',t[1])
コード例 #6
0
ファイル: rules.py プロジェクト: skeptycal/pascal-in-python
def p_program_start(t):
	'program : header SEMICOLON block DOT'
	t[0] = Node('program',t[1],t[3])
コード例 #7
0
ファイル: rules.py プロジェクト: skeptycal/pascal-in-python
def p_while_statement(t):
	"""while_statement : WHILE expression DO statement"""
	t[0] = Node('while',t[2],t[4])
コード例 #8
0
def p_integer(t):
    """ integer : INTEGER """
    t[0] = Node('int', t[1])
コード例 #9
0
def p_string(t):
    """ string : STRING """
    t[0] = Node('string', t[1])
コード例 #10
0
def p_block(t):
    """block : variable_declaration_part function statement_part
	"""
    t[0] = Node('block', t[1], t[2], t[3])
コード例 #11
0
def p_real(t):
    """ real : REAL """
    t[0] = Node('real', t[1])
コード例 #12
0
def p_identifier(t):
    """ identifier : IDENTIFIER """
    t[0] = Node('identifier', str(t[1]).lower())
コード例 #13
0
def p_function_call_inline(t):
    """ function_call_inline : identifier LPAREN param_list RPAREN"""
    t[0] = Node('function_call_inline', t[1], t[3])
コード例 #14
0
ファイル: rules.py プロジェクト: yitaodong/pascal-compiler
def p_procedure_declaration(t):
    """procedure_declaration : procedure_heading SEMICOLON block"""
    t[0] = Node("procedure", t[1], t[3])
コード例 #15
0
ファイル: rules.py プロジェクト: skeptycal/pascal-in-python
def p_parameter(t):
	""" parameter : identifier COLON type"""
	t[0] = Node("parameter", t[1], t[3])
コード例 #16
0
def p_char(t):
    """ char : CHAR """
    t[0] = Node('char', t[1])
コード例 #17
0
ファイル: rules.py プロジェクト: skeptycal/pascal-in-python
def p_type(t):
	""" type : TREAL 
	| TINTEGER
	| TCHAR
	| TSTRING """
	t[0] = Node('type',t[1].lower())
コード例 #18
0
def p_variable_declaration(t):
    """variable_declaration : type identifier SEMICOLON"""
    t[0] = Node('var', t[2],
                t[1])  #node will be as "var_list",identifier, type
コード例 #19
0
ファイル: rules.py プロジェクト: skeptycal/pascal-in-python
def p_param(t):
	""" param : expression """
	t[0] = Node("parameter",t[1])
コード例 #20
0
def p_function(t):
    """function : function_declaration SEMICOLON function
		| """

    if len(t) == 4:
        t[0] = Node('function_list', t[1], t[3])
コード例 #21
0
ファイル: rules.py プロジェクト: skeptycal/pascal-in-python
def p_repeat_statement(t):
	"""repeat_statement : REPEAT statement UNTIL expression"""
	t[0] = Node('repeat',t[2],t[4])
コード例 #22
0
def p_function_declaration(t):
    """ function_declaration : function_heading SEMICOLON block"""
    t[0] = Node('function', t[1], t[3])
コード例 #23
0
ファイル: rules.py プロジェクト: skeptycal/pascal-in-python
def p_assignment_statement(t):
	"""assignment_statement : identifier ASSIGNMENT expression"""
	t[0] = Node('assign',t[1],t[3])
コード例 #24
0
def p_parameter(t):
    """ parameter : type identifier"""
    t[0] = Node("parameter", t[2], t[1])
コード例 #25
0
ファイル: rules.py プロジェクト: skeptycal/pascal-in-python
def p_psign(t):
	"""psign : TIMES
	| DIVISION"""
	t[0] = Node('sign',t[1])
コード例 #26
0
ファイル: rules.py プロジェクト: yitaodong/pascal-compiler
def p_variable_declaration(t):
    """variable_declaration : identifier COLON type SEMICOLON"""
    t[0] = Node('var', t[1], t[3])