diff --git a/brainfuck.py b/brainfuck.py index 5037658..1f68df3 100644 --- a/brainfuck.py +++ b/brainfuck.py @@ -69,10 +69,6 @@ def interpret(prog): return ar -def format(prog,debug_mode=True): - if debug_mode: - return ''.join(i for i in prog if i in '[]<>+-.,#') - return ''.join(i for i in prog if i in '[]<>+-.,') def repl(): diff --git a/grammar.ebnf b/grammar.ebnf index 1d26dc1..de4d7d8 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -1,12 +1,13 @@ program = statement, {statement} block = "{",{statement}, "}" -statement = short_statement | while_block | if_block | function_def -short_statement = print | return | input | declaration | assignement | expression | inc-dec, ";" +statement = short_statement | while_block | if_block | function_def | function_bf +short_statement = print | return | input | declaration | assignement | expression | inc-dec | import, ";" assignement = name, assignement_symbol, expression declaration = "var" , name , {",",name} return = "return", expression +import = "import", string expression = compar_op | function_call @@ -14,9 +15,9 @@ function_call = name, '(',[expression,{','expression,}] compar = calcul, [compar_op, calcul] -calcul = terme, [('+'|'-'), terme] -terme = factor [("*"|"/"), factor] -factor = val ["**", val] +calcul = terme, {('+'|'-'), terme} +terme = factor {("*"|"/"), factor} +factor = val {"**", val} val = name | number | "(", expression, ")" inc-dec = name, ("++" | "--") @@ -24,9 +25,12 @@ condition = '(', expression, ')' while_block = "while", condition, block if_block = "if", condition, block, ["else", block] function_def = "def", name, '(',[name,{',',name}],')',block +function_bf = 'bf',name, '{', brainfuck_code, '}' + +brainfuck_code = {'<'|'>'|'+'|'-'|'.'|','|'['|']'} bin_op = "+"|"-"|"*"|"/"|"**" compar_op = ">"|">="|"<"|"<="|"==" -assignement_symbol = "="|"+="|"-="|"/="|"*=" \ No newline at end of file +assignement_symbol = "="|"+="|"-="|"/="|"*=" diff --git a/op.bf b/op.bf index 40176a6..aa1296f 100644 --- a/op.bf +++ b/op.bf @@ -11,6 +11,9 @@ COMPAR : {add the last part} +Situation à la fin: +0 b 0 k + EQUALS : +>[<->[-]]>>[<<<->>>-]<<< @@ -18,16 +21,16 @@ DIFFERENT : >[<+>[-]]>>[<<<+>>>-]<<< GREATER : ->>>[<<<+>>>-]<<< +>[-]>>[<<<+>>>-]<<< GREATER OR EQUALS : -+>[<->[-]]< ++>[<->[-]]>>[-]<<< SMALLER : ->[<+>[-]]< +>[<+>[-]]>>[-]<<< SMALLER OR EQUALS : -+>>>[<<<->>>-]<<< ++>[-]>>[<<<->>>-]<<< @@ -46,12 +49,31 @@ DIV : [->+>>+<<<] <[->+>>+<<<] >>> -[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]+>[<->[-]]< +[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]+>[<->[-]]>>[-]<<< [ -<[-<->>+<]>[-<+>] <<<+> [->>+>+<<<]>>>[-<<<+>>>]<< [->>+>+<<<]>>>[-<<<+>>>]< - <[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]>>>[<<<+>>>-]<<< + <[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]+>[<->[-]]>>[-]<<< ] <[-]<[-]< + + +MOD : +===== +[->+>>+<<<] +<[->+>>+<<<] +>>> +[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]+>[<->[-]]>>[-]<<< +[ + -<[-<->>+<]>[-<+>] + << + [->>+>+<<<]>>>[-<<<+>>>]<< + [->>+>+<<<]>>>[-<<<+>>>]< + <[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]+>[<->[-]]>>[-]<<< +] +<[-]<[-<+>]< + + + diff --git a/stdlib.mg b/stdlib.mg new file mode 100644 index 0000000..b2b623a --- /dev/null +++ b/stdlib.mg @@ -0,0 +1,33 @@ +/* +STDLIB +The comment after the bf function is the number of arguments +*/ + + +bf putc {{ //1 + . +}} + +bf scanc {{ //0 + >, +}} + +bf debug {{ //0 + #> +}} + +def print_int(n){ + var i, val; + i = 1; + while (i <= n){ + i *= 10; + } + i /= 10; + while (i >= 1){ + val = n / i; + n %= i; + putc(val + 48); + i /= 10; + } + return n; +} diff --git a/stdlib.py b/stdlib.py new file mode 100644 index 0000000..e0e6e60 --- /dev/null +++ b/stdlib.py @@ -0,0 +1,48 @@ +from collections import ChainMap + +import to_bf_compiler as compiler + +BUILT_IN_FUNCTIONS = ChainMap({}) + +BUILT_IN_FUNCTIONS['putc'] = [('PUTC',)] +BUILT_IN_FUNCTIONS['scanc'] =[('SCANC',)] +BUILT_IN_FUNCTIONS['debug'] = [('DEBUG',)] + +func_to_compile = [] + + + +func_to_compile.append( + ('print_int',""" + +def print_int(n){ + var i, val; + i = 1; + while (i <= n){ + i *= 10; + } + i /= 10; + while (i >= 1){ + val = n / i; + n %= i; + putc(val + 48); + i /= 10; + } + return n; +} +""")) + + + +def compile_func(name,func): + ast = compiler.compile(func,2) + ir = compiler.Ast_to_IR(ast,BUILT_IN_FUNCTIONS) #on peut utiliser toutes les fonctions définis avant + ir.convert() + return ir.functions[name] + + + +for name, func in func_to_compile: + BUILT_IN_FUNCTIONS[name] = compile_func(name,func) + + diff --git a/to_bf_compiler.py b/to_bf_compiler.py index f234250..2287e51 100644 --- a/to_bf_compiler.py +++ b/to_bf_compiler.py @@ -9,18 +9,19 @@ 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|def|return','KEYWORD'), + (r'if|while|else|var|def|return|bf|import','KEYWORD'), + (r'\{\{[^}]*\}\}','BRAINFUCK_CODE'), (r'>=|<=|==|>|<|!=','COMPAR_OP'), - (r'\+=|-=|\*=|/=|=','ASSIGNEMENT_OP'), + (r'\+=|%=|-=|\*=|/=|=','ASSIGNEMENT_OP'), (r'\+\+|--','INC_DEC_OP'), - (r'\+|\*\*|\*|-|/','BIN_OP'), + (r'\+|\*\*|\*|-|/|%','BIN_OP'), (r'\(|\)|\{|\}|;|,','CONTROL_CHAR'), (r'[a-zA-Z]\w*','IDENTIFIERS'), (r'\d[\d_]*','NUMBERS'), - (r"'.'",'CHAR') - + (r"'.'",'CHAR'), + (r'"(\\.|[^"\n])*"','STRING'), )] USELESS_TAGS = ('WHITESPACE','COMMENTS') @@ -47,6 +48,34 @@ def lex(program,keep_whitespace=False): def clean_tokens(tokens): return [token for token in tokens if token.tag not in USELESS_TAGS] +def format_bf(prog,debug_mode=True): + if debug_mode: + t = [] + index = 0 + while index < len(prog): + if prog[index] in '[]<>+-.,': + t.append(prog[index]) + index += 1 + elif prog[index] == '#': + t.append('#') + index += 1 + try: + if prog[index] == '(': + index += 1 + t.append('(') + while prog[index] != ')': + t.append(prog[index]) + index+=1 + t.append(')') + except IndexError: + pass + else: + index+=1 + return ''.join(t) + + return ''.join(i for i in prog if i in '[]<>+-.,') + + #**************************** @@ -57,12 +86,14 @@ BlockNode = namedtuple('BlockNode',('list_of_statement')) IfNode = namedtuple('IfNode',('condition','block','else_block')) WhileNode = namedtuple('WhileNode',('condition','block')) FunctionDefNode = namedtuple('FunctionDefNode',('name','args','block')) +BfDefNode = namedtuple('BfDefNode',('name','code')) AssignementNode = namedtuple('AssignementNode',('target','operator','expression')) IncDecNode = namedtuple('IncDecNode',('name','operator')) DeclarationNode = namedtuple('DeclarationNode',('names')) FunctionCallNode = namedtuple('FunctionCallNode',('name','args')) ReturnNode = namedtuple('ReturnNode',('expr')) +ImportNode = namedtuple('ImportNode',('path',)) BinOpNode = namedtuple('BinOpNode',('left','op','right')) IdentifierNode = namedtuple('IdentifierNode',('name')) @@ -105,6 +136,9 @@ class Parser: nodes = [] while 1: statement = self.statement() + if isinstance(statement,ImportNode): + ast = get_ast(statement.path) + nodes.extend(ast) if statement: nodes.append(statement) else: @@ -131,18 +165,15 @@ class Parser: return BlockNode(nodes) def statement(self): - #print(f"enter statement ({self.pos})") - for func in (self.short_statement,self.while_block, self.if_block,self.function_block): + for func in (self.short_statement,self.while_block, self.if_block,self.function_block, self.bf_block): node = func() if node: - #cprint("statement",'green') return node - #cprint("no statement",'red') return None def short_statement(self): #print(f"enter short_statement ({self.pos})") - for func in (self.declaration, self.assignement,self.inc_dec,self.expression,self.return_): + for func in (self.declaration, self.assignement,self.inc_dec,self.expression,self.return_,self.import_): node = func() if node: if self.accept_text(";"): @@ -153,6 +184,14 @@ class Parser: #cprint("no short_statement",'red') return None + def import_(self): + if not self.accept_text('import'): + return None + path = self.accept_tag('STRING') + if not path: + raise SyntaxError('Bad import') + return ImportNode(path.text[1:-1]) + def return_(self): if not self.accept_text('return'): return None @@ -260,19 +299,22 @@ class Parser: if not terme1: return None - op = None - sym = self.accept_text('+') - if sym: - op = sym - sym = self.accept_text('-') - if sym: - op = sym + while 1: + op = None + sym = self.accept_text('+') + if sym: + op = sym + sym = self.accept_text('-') + if sym: + op = sym + if not op: + return terme1 - if op: - terme2 = self.terme() - if not terme2: - raise SyntaxError("missing second term in calcul") - return BinOpNode(terme1,op.text,terme2) + if op: + terme2 = self.terme() + if not terme2: + raise SyntaxError("missing second term in calcul") + terme1 = BinOpNode(terme1,op.text,terme2) return terme1 @@ -282,19 +324,25 @@ class Parser: if not factor1: return None - op = None - sym = self.accept_text('*') - if sym: - op = sym - sym = self.accept_text('/') - if sym: - op = sym + while 1: + op = None + sym = self.accept_text('*') + if sym: + op = sym + sym = self.accept_text('/') + if sym: + op = sym + sym = self.accept_text('%') + if sym: + op = sym - if op: - factor2 = self.factor() - if not factor2: - raise SyntaxError("missing second factor in terme") - return BinOpNode(factor1,op.text,factor2) + if op: + factor2 = self.factor() + if not factor2: + raise SyntaxError("missing second factor in terme") + factor1 = BinOpNode(factor1,op.text,factor2) + else: + break return factor1 def factor(self): @@ -302,12 +350,11 @@ class Parser: val = self.value() if not val: return None - op = self.accept_text('**') - if op: - val2 = self.value() - if not val2: - raise SyntaxError("missing second val un factor") - return BinOpNode(val,op.text,val2) + while self.accept_text('**'): + val2 = self.value() + if not val2: + raise SyntaxError("missing second val un factor") + val = BinOpNode(val,'**',val2) return val def value(self): @@ -408,6 +455,19 @@ class Parser: return FunctionDefNode(name.text,args,block) + def bf_block(self): + if not self.accept_text('bf'): + return None + name = self.accept_tag('IDENTIFIERS') + if not name: + raise SyntaxError("A bf function need a name") + + code = self.accept_tag('BRAINFUCK_CODE') + if not code: + raise SyntaxError("Bad bf block") + code = format_bf(code.text[2:-2]) + return BfDefNode(name.text,code) + @@ -417,22 +477,23 @@ def parse(prog): return parser.program() +def get_ast(path): + with open(path) as file: + code = file.read() + tokens = lex(code) + ast = Parser(tokens).parse() + return ast.list_of_statement + + + #*************** -BUILT_IN_FUNCTIONS = ChainMap({ - 'putc':[('PUTC',)], - 'scanc':[('SCANC',)], - 'mod':[ - ('') - - ], - - }) - class Ast_to_IR: - def __init__(self,ast,functions=BUILT_IN_FUNCTIONS): + def __init__(self,ast,functions=None): + if functions is None: + functions = ChainMap({}) self.ast = ast self.functions = functions @@ -456,6 +517,9 @@ class Ast_to_IR: for statement in statements: if isinstance(statement,FunctionDefNode): self.functions[statement.name] = Ast_to_IR(statement,self.functions).convert() + elif isinstance(statement,BfDefNode): + code = statement.code + self.functions[statement.name] = [('BF',code,statement.name)] vars_ = [] @@ -573,6 +637,8 @@ methods = { WhileNode: Ast_to_IR.while_to_ir, IfNode: Ast_to_IR.if_to_ir, IncDecNode: Ast_to_IR.inc_dec_to_ir, + BfDefNode: lambda self,node:None, + ImportNode: lambda self,node:None, } default = Ast_to_IR.expression_statement_to_ir @@ -587,6 +653,10 @@ def init_function(nb,name): def end_function(nb,name): return '<[-]' * nb + '>' * nb + (f"[-{'<'*nb}+{'>'*nb}]" if nb else "") + '<' * nb+ f' END_FUNCTION {name}' +def bf(code,name): + return f"BF {name}\n{code}\nEND_BF" + + def assign(val,name): return f"{'<'*val}[-]{'>'*val}[-{'<'*val}+{'>'*val}]< ASSIGN {name}" @@ -614,22 +684,25 @@ def bin_op(op): elif op == '*': bf = "<[->>+<<]>[->[-<<+>>>+<]>[-<+>]<<]>[-]<<" elif op == '/': - bf = "[->+>>+<<<]<[->+>>+<<<]>>>[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]+>[<->[-]]<" - "[-<[-<->>+<]>[-<+>]<<<+>[->>+>+<<<]>>>[-<<<+>>>]<<[->>+>+<<<]>>>[-<<<+>>>]<<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>" - ">[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]>>>[<<<+>>>-]<<<#]<[-]<[-]<" - + bf = ("[->+>>+<<<]<[->+>>+<<<]>>>[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]+>[<->[-]]>>[-]<<<" + "[-<[-<->>+<]>[-<+>]<<<+>[->>+>+<<<]>>>[-<<<+>>>]<<[->>+>+<<<]>>>[-<<<+>>>]<<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>" + ">[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]+>[<->[-]]>>[-]<<<]<[-]<[-]<") + elif op == '%': + bf = "[->+>>+<<<]<[->+>>+<<<]>>>[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]+>[<->[-]]>>[-]<<<" \ + "[-<[-<->>+<]>[-<+>]<<[->>+>+<<<]>>>[-<<<+>>>]<<[->>+>+<<<]>>>[-<<<+>>>]<<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>" \ + "[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]+>[<->[-]]>>[-]<<<]<[-]<[-<+>]<" elif op == '==': bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + "+>[<->[-]]>>[<<<->>>-]<<<" elif op == '!=': bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + ">[<+>[-]]>>[<<<+>>>-]<<<" elif op == '>': - bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + ">>>[<<<+>>>-]<<<" + bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + ">[-]>>[<<<+>>>-]<<<" elif op == '>=': - bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + "+>[<->[-]]<" + bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + "+>[<->[-]]>>[-]<<<" elif op == '<': - bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + ">[<+>[-]]<" + bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + ">[<+>[-]]>>[-]<<<" elif op == '<=': - bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + "+>>>[<<<->>>-]<<<" + bf = "<[>[->+>+<<]>>[-<<+>>]+<[>-<[-]]>[->+>+<<]>>[-<<+>>]<[<<<<[-]+>+>>>-]<<<-<-]" + "+>[-]>>[<<<->>>-]<<<" else: raise Exception(f"bin_op {op} not implemented yet") @@ -654,12 +727,6 @@ 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)