Browse Source

First commit

master
miggazElquez 1 year ago
commit
f57187515f
4 changed files with 752 additions and 0 deletions
  1. +3
    -0
      .gitignore
  2. +201
    -0
      brainfuck.py
  3. +26
    -0
      grammar.ebnf
  4. +522
    -0
      to_bf_compiler.py

+ 3
- 0
.gitignore View File

@ -0,0 +1,3 @@
__pycache__
*.pyc

+ 201
- 0
brainfuck.py View File

@ -0,0 +1,201 @@
"""interpreter of brainfuck written in python"""
import sys
import os
from termcolor import cprint
os.system('color')
class BFError(RuntimeError):
pass
def pair_bracket(prog):
result = []
brackets = []
for index, instr in enumerate(prog):
if instr == '[':
brackets.append(index)
if instr == ']':
open_brackets = brackets.pop()
result.append((open_brackets,index))
return dict(result)
def interpret(prog):
ar = [0]
ptr = 0
index = 0
blocks = []
bracket_info = pair_bracket(prog)
while index < len(prog):
instr = prog[index]
if instr == '+':
ar[ptr] += 1
elif instr == '-':
ar[ptr] -= 1
elif instr == '>':
ptr += 1
if ptr == len(ar):
ar.append(0)
elif instr == '<':
ptr -= 1
if ptr < 0:
raise ValueError("You can't go to a cell before 0")
elif instr == '.':
print(chr(ar[ptr]),end='')
elif instr == ',':
val = input()
if len(val) > 1:
raise ValueError('please put character one by one')
if val:
ar[ptr] = ord(val)
else:
ar[ptr] = 0
elif instr == '[':
if ar[ptr]:
blocks.append(index)
else:
index = bracket_info[index]
elif instr == ']':
if ar[ptr]:
index = blocks.pop()
continue
elif instr == '#':
print(ar, ptr, ar[ptr])
index += 1
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():
"""
use brainfuck in an interactive mode
special commands :
p: where is the pointer
v: val of the cell (the number, not the ascii val)
a: show the entire array
"""
ar = [0]
ptr = 0
blocks = []
while 1:
try:
not_ok = True
while not_ok:
try:
prog = input('> ')
not_ok = False
except KeyboardInterrupt:
print()
except EOFError:
break
if prog == 'p':
print(ptr)
elif prog == 'v':
print(ar[ptr])
elif prog == 'a':
print(ar)
elif prog == 'help':
repl_help()
elif prog == 'q':
break
elif prog == 'r':
repl()
return
else:
index = 0
bracket_info = pair_bracket(prog)
try:
while index < len(prog):
instr = prog[index]
if instr == '+':
ar[ptr] += 1
elif instr == '-':
ar[ptr] -= 1
elif instr == '>':
ptr += 1
if ptr == len(ar):
ar.append(0)
elif instr == '<':
ptr -= 1
if ptr < 0:
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='')
elif instr == ',':
val = input()
if len(val) > 1:
raise ValueError('please put character one by one')
if val:
ar[ptr] = ord(val)
else:
ar[ptr] = 0
elif instr == '[':
if ar[ptr]:
blocks.append(index)
else:
index = bracket_info[index]
elif instr == ']':
if ar[ptr]:
index = blocks.pop()
continue
else:
blocks.pop()
elif instr == '#':
try:
if prog[index+1] == '(':
index = index+2
while prog[index] != ')':
print(prog[index],end='')
index += 1
print(' ',end='')
except IndexError:
pass
print(ar, ptr, ar[ptr])
index += 1
except KeyboardInterrupt:
pass
except BFError as e:
cprint(e,'red')
if '.' in prog:
print()
def repl_help():
print("""\
In this repl, you can enter any valid brainfuck expression.
There is also some special command :
'a' will show you the state of the memory
'p' will show you where is the pointer
'v' will show you the value of the cell
'r' will reset the array""")
def main():
if len(sys.argv) == 2:
interpret(open(sys.argv[2]).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])
else:
repl()
if __name__ == '__main__':
main()

+ 26
- 0
grammar.ebnf View File

@ -0,0 +1,26 @@
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}, ";"
expression = compar
compar = calcul, [compar_op, calcul]
calcul = terme, [('+'|'-'), terme]
terme = factor [("*"|"/"), factor]
factor = val ["**", val]
val = name | number | "(", expression, ")"
inc-dec = name, ("++" | "--")
condition = '(', expression, ')'
while_block = "while", condition, block
if_block = "if", condition, block, ["else", block]
bin_op = "+"|"-"|"*"|"/"|"**"
compar_op = ">"|">="|"<"|"<="|"=="
assignement_symbol = "="|"+="|"-="|"/="|"*="

+ 522
- 0
to_bf_compiler.py View File

@ -0,0 +1,522 @@
import re
from collections import namedtuple
import os
from termcolor import cprint
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'>=|<=|==|>|<','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')
)]
USELESS_TAGS = ('WHITESPACE','COMMENTS')
Token = namedtuple('Token',('text','tag'))
def lex(program,keep_whitespace=False):
pos = 0
tokens = []
while pos < len(program):
for regex, tag in REGEXS:
match = regex.match(program,pos)
if match is not None:
token = match.group(0)
tokens.append(Token(token,tag))
pos = match.end(0)
break
else:
raise SyntaxError(f"No match find during the lexing for {program[pos:pos+20]}...")
if keep_whitespace:
return tokens
return clean_tokens(tokens)
def clean_tokens(tokens):
return [token for token in tokens if token.tag not in USELESS_TAGS]
#****************************
ProgramNode = namedtuple('ProgramNode',('list_of_statement'))
BlockNode = namedtuple('BlockNode',('list_of_statement'))
IfNode = namedtuple('IfNode',('condition','block','else_block'))
WhileNode = namedtuple('WhileNode',('condition','block'))
AssignementNode = namedtuple('AssignementNode',('target','operator','expression'))
IncDecNode = namedtuple('IncDecNode',('name','operator'))
DeclarationNode = namedtuple('DeclarationNode',('names'))
BinOpNode = namedtuple('BinOpNode',('left','op','right'))
IdentifierNode = namedtuple('IdentifierNode',('name'))
NumberNode = namedtuple('NumberNode',('value'))
class Parser:
def __init__(self,tokens):
self.tokens = tokens
self.pos = 0
def parse(self):
return self.program()
def accept_text(self,value):
try:
token = self.tokens[self.pos]
except IndexError:
#print("reach the end")
return False
if token.text == value:
self.pos += 1
return token
return False
def accept_tag(self,tag):
try:
token = self.tokens[self.pos]
except IndexError:
return False
if token.tag == tag:
self.pos += 1
return token
return False
def program(self):
nodes = []
while 1:
statement = self.statement()
if statement:
nodes.append(statement)
else:
break
if not len(nodes):
raise ValueError("Your program seems to be empty")
return ProgramNode(nodes)
def block(self):
#print(f"enter block ({self.pos})")
if not self.accept_text("{"):
return None
nodes = []
while 1:
statement = self.statement()
if statement:
nodes.append(statement)
else:
break
if not self.accept_text('}'):
raise SyntaxError("'}' missing at the end of the block")
#cprint("block",'green')
return BlockNode(nodes)
def statement(self):
#print(f"enter statement ({self.pos})")
for func in (self.short_statement,self.while_block, self.if_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,):
node = func()
if node:
if self.accept_text(";"):
#cprint('short_statement','green')
return node
else:
raise SyntaxError(f"Missing ';' ({node})")
#cprint("no short_statement",'red')
return None
def declaration(self):
if not self.accept_text('var'):
return None
name = self.accept_tag('IDENTIFIERS')
if not name:
raise SyntaxError("You must specifies an identifiers after a 'var' keyword")
names = [IdentifierNode(name.text)]
while 1:
if self.accept_text(','):
name = self.accept_tag('IDENTIFIERS')
if name:
names.append(IdentifierNode(name.text))
else:
raise SyntaxError("Expected an identifiers after a ',' in a declaration statemement")
else:
break
return DeclarationNode(names)
def assignement(self):
#print(f"enter assignement ({self.pos})")
name = self.accept_tag('IDENTIFIERS')
if not name:
#cprint("no assignement (no name)",'red')
return None
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:
raise SyntaxError("Missing expression in a assignement")
#cprint("assignement",'green')
return AssignementNode(IdentifierNode(name.text),sym.text,expr)
def inc_dec(self):
#print(f"enter inc_dec ({self.pos})")
name = self.accept_tag('IDENTIFIERS')
if not name:
#cprint("no inc_dec (no name)",'red')
return None
op = self.accept_tag('INC_DEC_OP')
if not op:
self.pos -= 1 #for the name
#cprint("no inc_dec (no op)",'red')
return None
#cprint("inc_dec",'green')
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 compar(self):
calcul1 = self.calcul()
if not calcul1:
return None
op = self.accept_tag('COMPAR_OP')
if op:
calcul2 = self.calcul()
if not calcul2:
raise SyntaxError("missing second term in calcul")
return BinOpNode(calcul1,op.text,calcul2)
return calcul1
def calcul(self):
#print(f"enter calcul ({self.pos})")
"""There is calcul, terme, factor and val for differentiate between +/-, * or /, and **"""
terme1 = self.terme()
if not terme1:
return None
op = None
sym = self.accept_text('+')
if sym:
op = sym
sym = self.accept_text('-')
if sym:
op = sym
if op:
terme2 = self.terme()
if not terme2:
raise SyntaxError("missing second term in calcul")
return BinOpNode(terme1,op.text,terme2)
return terme1
def terme(self):
#print(f"enter terme ({self.pos})")
factor1 = self.factor()
if not factor1:
return None
op = None
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)
return factor1
def factor(self):
#print(f"enter factor ({self.pos})")
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)
return val
def value(self):
#print(f"enter value ({self.pos})")
name = self.accept_tag('IDENTIFIERS')
if name:
return IdentifierNode(name.text)
number = self.accept_tag('NUMBERS')
if number:
return NumberNode(int(number.text))
if self.accept_text('('):
expr = self.expression()
if not (expr and self.accept_text(')')):
raise SyntaxError("missing expression or matching ')'")
return expr
return None
def while_block(self):
#print(f"enter while_block ({self.pos})")
if not self.accept_text('while'):
#cprint("no while_block (keyword)",'red')
return None
if not self.accept_text('('):
raise SyntaxError("Bad while block")
cond = self.expression()
if not cond:
raise SyntaxError("Bad while block")
if not self.accept_text(')'):
raise SyntaxError("Bad while block")
block = self.block()
if not block:
raise SyntaxError("Bad while block")
#cprint("while_block",'green')
return WhileNode(cond,block)
def if_block(self):
#print(f"enter if_block ({self.pos})")
if not self.accept_text('if'):
#cprint("no if_block (keyword)",'red')
return None
if not self.accept_text('('):
raise SyntaxError("Bad if block")
cond = self.expression()
if not cond:
raise SyntaxError("Bad if block")
if not self.accept_text(')'):
raise SyntaxError("Bad if block")
block = self.block()
if not block:
raise SyntaxError("Bad if block")
if self.accept_text('else'):
else_block = self.block()
if not else_block:
raise SyntaxError("Bad else block")
return IfNode(cond,block,else_block)
#cprint("if_block",'green')
return IfNode(cond,block,None)
def parse(prog):
tokens = clean_tokens(lex(prog))
parser = Parser(tokens)
return parser.program()
#***************
class Ast_to_IR:
def __init__(self,ast):
self.ast = ast
def add(self,val):
self.ir.append(val)
def convert(self):
self.ir = []
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_
self.pos = 0
self.add(('INIT',len(vars_)))
self.push_block(self.ast)
return self.ir
def push_block(self,node):
for statement in node.list_of_statement:
func = methods.get(type(statement),default) #switch-case
func(self,statement)
def assignement_to_ir(self,node):
if node.operator == '=':
expression = node.expression
else:
operator = node.operator[0] #ex : '+=' -> '+'
expression = BinOpNode(node.target,operator,node.expression)
self.push_expr(expression)
assign_distance = self.pos + self.vars[node.target]
self.add(('ASSIGN',assign_distance,node.target.name))
self.pos -= 1
def expression_statement_to_ir(self,node):
self.push_expr(node)
self.pos -= 1
self.add(('POP',))
def inc_dec_to_ir(self,node):
assign_distance = self.pos + self.vars[node.name]
if node.operator == "++":
self.add(('INC',assign_distance,node.name.name))
else:
self.add(('DEC',assign_distance,node.name.name))
def push_expr(self,node):
if isinstance(node,NumberNode):
self.pos += 1
self.add(('PUSH_NUMBER',node.value))
return
if isinstance(node,IdentifierNode):
self.pos += 1
distance = self.pos + self.vars[node]
self.add(('PUSH_VAR',distance,node.name))
return
if isinstance(node,BinOpNode):
self.push_expr(node.left)
self.push_expr(node.right)
self.pos -= 1
self.add(('BIN_OP',node.op))
return
raise Exception(f"cette expression n'est pas bonne : {node}")
def while_to_ir(self,node):
self.push_expr(node.condition)
self.add(('WHILE_ENTER',))
self.pos -= 1
self.push_block(node.block)
self.push_expr(node.condition)
self.add(('WHILE_END',))
self.pos -= 1
def if_to_ir(self,node):
self.push_expr(node.condition)
self.add(('IF_ENTER',))
self.pos += 1
self.push_block(node.block)
if node.else_block:
self.add(('ELSE_ENTER',))
self.pos -= 2
self.push_block(node.else_block)
self.add(('ELSE_END',))
else:
self.add(('IF_END',))
self.pos -= 2
methods = {
DeclarationNode: lambda self,node:None,
AssignementNode: Ast_to_IR.assignement_to_ir,
WhileNode: Ast_to_IR.while_to_ir,
IfNode: Ast_to_IR.if_to_ir,
IncDecNode: Ast_to_IR.inc_dec_to_ir,
}
default = Ast_to_IR.expression_statement_to_ir
#************
def init(nb):
return '>' * (nb-1) + f' INIT {nb}'
def assign(val,name):
return f"{'<'*val}[-]{'>'*val}[-{'<'*val}+{'>'*val}]< ASSIGN {name}"
def inc(val,name):
return f"{'<'*val}+{'>'*val} INC {name}"
def dec(val,name):
return f"{'<'*val}-{'>'*val} DEC {name}"
def pop():
return '[-]< POP'
def push_number(val):
return ">" + "+"*val + f" PUSH_NUMBER {val}"
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 == "+":
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]}"
def while_enter():
return "[[-]< WHILE_ENTER"
def while_end():
return "]< WHILE_END"
def if_enter():
return ">+<[[-]>- IF_ENTER"
def else_enter():
return "<]>[-<< ELSE_ENTER"
def else_end():
return ">>]<< ELSE_END"
def if_end():
return "<]>[-]<< IF_END"
def compile_ir(ir):
return '\n'.join(globals()[i[0].lower()](*i[1:]) for i in ir)
#**************
def compile(prog):
tokens = lex(prog)
ast = Parser(tokens).parse()
ir = Ast_to_IR(ast).convert()
brainfuck = compile_ir(ir)
return brainfuck

Loading…
Cancel
Save