From 287a2a066afb6b535e83597badc5d8a330e464b1 Mon Sep 17 00:00:00 2001 From: miggazElquez Date: Thu, 5 Sep 2019 21:00:08 +0200 Subject: [PATCH] add functions and some operators 2 --- brainfuck.py | 8 +- grammar.ebnf | 16 +++- to_bf_compiler.py | 236 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 222 insertions(+), 38 deletions(-) diff --git a/brainfuck.py b/brainfuck.py index 42f4526..5037658 100644 --- a/brainfuck.py +++ b/brainfuck.py @@ -134,7 +134,7 @@ def repl(): ptr = 0 raise BFError(f"You can't go to a cell before 0 (code before : {prog[:index]}, code after : {prog[index:]})") elif instr == '.': - print(chr(ar[ptr]),end='') + print(chr(ar[ptr]),end='',flush=True) elif instr == ',': val = input() if len(val) > 1: @@ -159,7 +159,7 @@ def repl(): if prog[index+1] == '(': index = index+2 while prog[index] != ')': - print(prog[index],end='') + print(prog[index],end='',flush=True) index += 1 print(' ',end='') except IndexError: @@ -189,10 +189,10 @@ There is also some special command : def main(): if len(sys.argv) == 2: - interpret(open(sys.argv[2]).read()) + interpret(open(sys.argv[1]).read()) elif len(sys.argv) == 3: if sys.argv[1] == '-i': #oui c'est de lam erde comme manière de faire - interpret(sys.argv[2]) + interpret(sys.argv[1]) else: repl() diff --git a/grammar.ebnf b/grammar.ebnf index 5279088..1d26dc1 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -1,12 +1,17 @@ program = statement, {statement} block = "{",{statement}, "}" -statement = short_statement | while_block | if_block -short_statement = declaration | assignement | expression | inc-dec, ";" -assignement = name, assignement_symbol, expression -declaration = "var" , name , {",",name}, ";" +statement = short_statement | while_block | if_block | function_def +short_statement = print | return | input | declaration | assignement | expression | inc-dec, ";" + +assignement = name, assignement_symbol, expression +declaration = "var" , name , {",",name} +return = "return", expression + +expression = compar_op | function_call + +function_call = name, '(',[expression,{','expression,}] -expression = compar compar = calcul, [compar_op, calcul] calcul = terme, [('+'|'-'), terme] @@ -18,6 +23,7 @@ inc-dec = name, ("++" | "--") condition = '(', expression, ')' while_block = "while", condition, block if_block = "if", condition, block, ["else", block] +function_def = "def", name, '(',[name,{',',name}],')',block diff --git a/to_bf_compiler.py b/to_bf_compiler.py index 0f3108c..f234250 100644 --- a/to_bf_compiler.py +++ b/to_bf_compiler.py @@ -1,5 +1,5 @@ import re -from collections import namedtuple +from collections import namedtuple, ChainMap import os from termcolor import cprint @@ -9,16 +9,17 @@ os.system('color') REGEXS = [(re.compile(i),j) for i,j in ( (r'\s+','WHITESPACE'), (r'//[^\n]*|/\*(?s:.)*\*/','COMMENTS'), - (r'if|for|while|else|var','KEYWORD'), + (r'if|for|while|else|var|def|return','KEYWORD'), - (r'>=|<=|==|>|<','COMPAR_OP'), + (r'>=|<=|==|>|<|!=','COMPAR_OP'), (r'\+=|-=|\*=|/=|=','ASSIGNEMENT_OP'), (r'\+\+|--','INC_DEC_OP'), (r'\+|\*\*|\*|-|/','BIN_OP'), (r'\(|\)|\{|\}|;|,','CONTROL_CHAR'), (r'[a-zA-Z]\w*','IDENTIFIERS'), - (r'\d[\d_]*','NUMBERS') + (r'\d[\d_]*','NUMBERS'), + (r"'.'",'CHAR') )] @@ -50,15 +51,18 @@ def clean_tokens(tokens): #**************************** -ProgramNode = namedtuple('ProgramNode',('list_of_statement')) +ProgramNode = namedtuple('ProgramNode',('list_of_statement','name')) BlockNode = namedtuple('BlockNode',('list_of_statement')) IfNode = namedtuple('IfNode',('condition','block','else_block')) WhileNode = namedtuple('WhileNode',('condition','block')) +FunctionDefNode = namedtuple('FunctionDefNode',('name','args','block')) AssignementNode = namedtuple('AssignementNode',('target','operator','expression')) IncDecNode = namedtuple('IncDecNode',('name','operator')) DeclarationNode = namedtuple('DeclarationNode',('names')) +FunctionCallNode = namedtuple('FunctionCallNode',('name','args')) +ReturnNode = namedtuple('ReturnNode',('expr')) BinOpNode = namedtuple('BinOpNode',('left','op','right')) IdentifierNode = namedtuple('IdentifierNode',('name')) @@ -107,7 +111,7 @@ class Parser: break if not len(nodes): raise ValueError("Your program seems to be empty") - return ProgramNode(nodes) + return ProgramNode(nodes,'__main__') #plus simple pour le debug def block(self): #print(f"enter block ({self.pos})") @@ -128,7 +132,7 @@ class Parser: def statement(self): #print(f"enter statement ({self.pos})") - for func in (self.short_statement,self.while_block, self.if_block): + for func in (self.short_statement,self.while_block, self.if_block,self.function_block): node = func() if node: #cprint("statement",'green') @@ -138,7 +142,7 @@ class Parser: def short_statement(self): #print(f"enter short_statement ({self.pos})") - for func in (self.declaration, self.assignement,self.inc_dec,self.expression,): + for func in (self.declaration, self.assignement,self.inc_dec,self.expression,self.return_): node = func() if node: if self.accept_text(";"): @@ -149,6 +153,14 @@ class Parser: #cprint("no short_statement",'red') return None + def return_(self): + if not self.accept_text('return'): + return None + expr = self.expression() + if not expr: + raise SyntaxError("Bad Return") + return ReturnNode(expr) + def declaration(self): if not self.accept_text('var'): return None @@ -162,10 +174,10 @@ class Parser: if name: names.append(IdentifierNode(name.text)) else: - raise SyntaxError("Expected an identifiers after a ',' in a declaration statemement") + raise SyntaxError("Expected an identifiers after a ',' in a declaration statement") else: break - + return DeclarationNode(names) def assignement(self): @@ -177,7 +189,6 @@ class Parser: sym = self.accept_tag('ASSIGNEMENT_OP') if not sym: self.pos -= 1 #for the name - #cprint("no assignement (no op)",'red') return None expr = self.expression() if not expr: @@ -200,15 +211,35 @@ class Parser: return IncDecNode(IdentifierNode(name.text),op.text) def expression(self): - #print(f"enter expression ({self.pos})") compar = self.compar() if compar: - #cprint("expression",'green') return compar - #cprint("no expression",'red') return None + def call_function(self): + name = self.accept_tag('IDENTIFIERS') + if not name: + return None + if not self.accept_text('('): + self.pos -= 1 + return None + + expr = self.expression() + args = [] + if expr: + args.append(expr) + while self.accept_text(','): + expr = self.expression() + if not expr: + raise SyntaxError("missing argument") + args.append(expr) + if not self.accept_text(')'): + raise SyntaxError("A function call must have a ')' at the end") + + return FunctionCallNode(name.text,args) + + def compar(self): calcul1 = self.calcul() if not calcul1: @@ -280,13 +311,23 @@ class Parser: return val def value(self): - #print(f"enter value ({self.pos})") + #print(f"enter value ({self.pos})") + func = self.call_function() #The orders matters !!! + if func: + return func + name = self.accept_tag('IDENTIFIERS') if name: return IdentifierNode(name.text) + number = self.accept_tag('NUMBERS') if number: return NumberNode(int(number.text)) + + char = self.accept_tag('CHAR') + if char: + return NumberNode(ord(char.text[1])) + if self.accept_text('('): expr = self.expression() if not (expr and self.accept_text(')')): @@ -340,6 +381,35 @@ class Parser: #cprint("if_block",'green') return IfNode(cond,block,None) + def function_block(self): + if not self.accept_text('def'): + return None + name = self.accept_tag('IDENTIFIERS') + if not name: + raise SyntaxError("A function need a name") + if not self.accept_text('('): + raise SyntaxError("Bad function block") + arg = self.accept_tag('IDENTIFIERS') + args = [] + if arg: + args.append(IdentifierNode(arg.text)) + while self.accept_text(','): + arg = self.accept_tag('IDENTIFIERS') + if not arg: + raise SyntaxError("Bad function block") + args.append(IdentifierNode(arg.text)) + + if not self.accept_text(')'): + raise SyntaxError("Bad function block") + + block = self.block() + if not block: + raise SyntaxError("Bad function block") + + return FunctionDefNode(name.text,args,block) + + + def parse(prog): tokens = clean_tokens(lex(prog)) @@ -349,27 +419,59 @@ def parse(prog): #*************** +BUILT_IN_FUNCTIONS = ChainMap({ + 'putc':[('PUTC',)], + 'scanc':[('SCANC',)], + 'mod':[ + ('') + + ], + + }) + class Ast_to_IR: - def __init__(self,ast): + def __init__(self,ast,functions=BUILT_IN_FUNCTIONS): self.ast = ast + self.functions = functions def add(self,val): self.ir.append(val) def convert(self): + + if isinstance(self.ast,ProgramNode): + statements = self.ast.list_of_statement + args = [] + block = self.ast + elif isinstance(self.ast,FunctionDefNode): + statements = self.ast.block.list_of_statement + args = self.ast.args + block = self.ast.block + + self.ir = [] + self.functions = self.functions.new_child() + for statement in statements: + if isinstance(statement,FunctionDefNode): + self.functions[statement.name] = Ast_to_IR(statement,self.functions).convert() + + vars_ = [] - for statemement in self.ast.list_of_statement: - if isinstance(statemement,DeclarationNode): - vars_.extend(statemement.names) - vars_ = {val:index for index,val in enumerate(vars_)} - self.vars = vars_ + for statement in statements: + if isinstance(statement,DeclarationNode): + vars_.extend(statement.names) + + all_vars = {val:index for index,val in enumerate((args+vars_)[::-1])} + self.vars = all_vars self.pos = 0 - self.add(('INIT',len(vars_))) - self.push_block(self.ast) + nb_decalage = len(vars_) - (1 if isinstance(self.ast,ProgramNode) else 0) + self.add(('INIT_FUNCTION',nb_decalage ,self.ast.name)) + self.push_block(block) + if isinstance(self.ast,FunctionDefNode): + self.add(('END_FUNCTION',len(all_vars),self.ast.name)) return self.ir def push_block(self,node): @@ -390,6 +492,9 @@ class Ast_to_IR: self.add(('ASSIGN',assign_distance,node.target.name)) self.pos -= 1 + def return_to_ir(self,node): + self.push_expr(node.expr) + def expression_statement_to_ir(self,node): self.push_expr(node) self.pos -= 1 @@ -403,6 +508,19 @@ class Ast_to_IR: self.add(('DEC',assign_distance,node.name.name)) def push_expr(self,node): + + if isinstance(node,FunctionCallNode): + for arg in node.args: + self.push_expr(arg) + + try: + self.ir.extend(self.functions[node.name]) + except KeyError: + raise SyntaxError(f"The function {node.name} doesn't exist") + self.pos -= len(node.args) + self.pos += 1 + return + if isinstance(node,NumberNode): self.pos += 1 self.add(('PUSH_NUMBER',node.value)) @@ -449,7 +567,9 @@ class Ast_to_IR: methods = { DeclarationNode: lambda self,node:None, + FunctionDefNode: lambda self,node:None, AssignementNode: Ast_to_IR.assignement_to_ir, + ReturnNode: Ast_to_IR.return_to_ir, WhileNode: Ast_to_IR.while_to_ir, IfNode: Ast_to_IR.if_to_ir, IncDecNode: Ast_to_IR.inc_dec_to_ir, @@ -459,8 +579,14 @@ default = Ast_to_IR.expression_statement_to_ir #************ -def init(nb): - return '>' * (nb-1) + f' INIT {nb}' + + +def init_function(nb,name): + return '>' * nb + f' INIT_FUNCTION {name}' + +def end_function(nb,name): + return '<[-]' * nb + '>' * nb + (f"[-{'<'*nb}+{'>'*nb}]" if nb else "") + '<' * nb+ f' END_FUNCTION {name}' + def assign(val,name): return f"{'<'*val}[-]{'>'*val}[-{'<'*val}+{'>'*val}]< ASSIGN {name}" @@ -481,13 +607,34 @@ def push_var(val,name): return '<' * (val-1) + f"[-{'>'*val}+>+<{'<'*val}]" + f"{'>'*val}>[-<{'<'*val}+>{'>'*val}]<" + f" PUSH_VAR {name}" def bin_op(op): - if op == "+": + if op == '+': bf = "[-<+>]<" - elif op == "-": + elif op == '-': bf = "[-<->]<" + elif op == '*': + bf = "<[->>+<<]>[->[-<<+>>>+<]>[-<+>]<<]>[-]<<" + elif op == '/': + bf = "[->+>>+<<<]<[->+>>+<<<]>>>[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]+>[<->[-]]<" + "[-<[-<->>+<]>[-<+>]<<<+>[->>+>+<<<]>>>[-<<<+>>>]<<[->>+>+<<<]>>>[-<<<+>>>]<<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>" + ">[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]>>>[<<<+>>>-]<<<#]<[-]<[-]<" + + elif op == '==': + bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + "+>[<->[-]]>>[<<<->>>-]<<<" + elif op == '!=': + bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + ">[<+>[-]]>>[<<<+>>>-]<<<" + elif op == '>': + bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + ">>>[<<<+>>>-]<<<" + elif op == '>=': + bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + "+>[<->[-]]<" + elif op == '<': + bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + ">[<+>[-]]<" + elif op == '<=': + bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + "+>>>[<<<->>>-]<<<" + else: raise Exception(f"bin_op {op} not implemented yet") - return bf + f" BIN_OP {op if op not in ('+','-') else {'+':'plus','-':'minus'}[op]}" + return bf + f" BIN_OP " +\ + (op if op not in ('+','-','<','>','<=','>=') else {'+':'plus','-':'minus','>':'gt','<':'st','>=':'gt_eq','<=':'st_eq'}[op]) def while_enter(): return "[[-]< WHILE_ENTER" @@ -507,16 +654,47 @@ def else_end(): def if_end(): return "<]>[-]<< IF_END" +def putc(): + return ". PRINT" + +def scanc(): + return ", INPUT" + + def compile_ir(ir): return '\n'.join(globals()[i[0].lower()](*i[1:]) for i in ir) + #************** -def compile(prog): +def compile(prog,level=4): tokens = lex(prog) + if level == 1: + return tokens ast = Parser(tokens).parse() + if level == 2: + return ast ir = Ast_to_IR(ast).convert() + if level == 3: + return ir brainfuck = compile_ir(ir) return brainfuck + +def main(): + import sys + try: + file = sys.argv[1] + except IndexError: + raise Exception("You must set a file") + + prog = open(file).read() + bf_prog = compile(prog) + with open(os.path.splitext(file)[0]+'.bf','w') as f: + f.write(bf_prog) + + +if __name__ == '__main__': + main() +