So I am using a python package sly which has a lexer and parser class. I am making my own programming language called NoobPy. So currently, the code will open test.noob and read each line and parse it. Now, if I were to define a variable, let's say x
, and just write x
in a line, it would print it, and I don't want that. I want it to print only if it's passed in the say
function which I made.
Lexer class
class NoobpyLexer(Lexer):
tokens = {NUMBER, STRING, FALSE, TRUE, NAME, WHILE, IF, ELSE, SAY,
PLUS, MINUS, TIMES, DIVIDE, ASSIGN,
EQ, LT, LE, GT, GE, NEQ}
literals = {'(', ')', ':'}
# String containing ignored characters
ignore = ' \t'
# Regular expression rules for tokens
STRING = r'\".*?\"'
PLUS = r'\+'
MINUS = r'-'
TIMES = r'\*'
DIVIDE = r'/'
EQ = r'=='
NEQ = r'!='
ASSIGN = r'='
LE = r'<='
GE = r'>='
LT = r'<'
GT = r'>'
@_(r'\d+')
def NUMBER(self, t):
t.value = int(t.value)
return t
# @_(r'^((true$|false$)$)')
# def BOOL(self, t):
# return t
@_(r'true')
def TRUE(self, t):
return t
@_(r'false')
def FALSE(self, t):
return t
# Identifiers and keywords
NAME = r'\b(?!((true$|false$)$)\b)\w+' # [a-zA-Z_][a-zA-Z0-9_]*$
NAME['if'] = IF
NAME['else'] = ELSE
NAME['while'] = WHILE
NAME['say'] = SAY
ignore_comment = r'\#.*'
# Line number tracking
@_(r'\n+')
def ignore_newline(self, t):
self.lineno += t.value.count('\n')
def error(self, t):
print("t: ", t)
print('Line %d: Bad character %r' % (self.lineno, t.value[0]))
self.index += 1
Parser class
class NoobpyParser(Parser):
# Get the token list from the lexer (required)
tokens = NoobpyLexer.tokens
log = logging.getLogger()
log.setLevel(logging.ERROR)
# debugfile = 'parser.out'
precedence = (
('left', PLUS, MINUS),
('left', TIMES, DIVIDE),
('right', UMINUS)
)
def __init__(self):
self.variables = {}
@_('')
def statement(self, p):
pass
@_('SAY expr')
def statement(self, p):
return 'say', p.expr
@_('NAME')
def expr(self, p):
return 'var', p.NAME
@_('var_assign')
def statement(self, p):
return p.var_assign
@_('NAME ASSIGN expr')
def var_assign(self, p):
return 'var_assign', p.NAME, p.expr
@_('expr')
def statement(self, p):
return p.expr
@_('expr PLUS expr')
def expr(self, p):
return 'add', p.expr0, p.expr1
@_('expr MINUS expr')
def expr(self, p):
return 'sub', p.expr0, p.expr1
@_('expr TIMES expr')
def expr(self, p):
return 'mul', p.expr0, p.expr1
@_('expr DIVIDE expr')
def expr(self, p):
return 'div', p.expr0, p.expr1
@_('MINUS expr %prec UMINUS')
def expr(self, p):
expression = list(p.expr)
if isinstance(expression[1], tuple):
res = 0
for i in expression[1]:
res += i
expression[1] = res
expression[1] = -expression[1]
return expression
@_('expr EQ expr')
def expr(self, p):
return 'eq', p.expr0, p.expr1
@_('"(" expr ")"')
def expr(self, p):
return p.expr
@_('NUMBER')
def expr(self, p):
return 'num', p.NUMBER
@_('STRING')
def expr(self, p):
return 'str', p.STRING
@_('TRUE')
def expr(self, p):
return p.TRUE
@_('FALSE')
def expr(self, p):
return p.FALSE
Execute class
class NoobpyExecute:
def __init__(self, tree, variables):
self.variables = variables
result = self.walkTree(tree)
if result is None:
pass
elif result is not None and type(result) in [int, float]:
print(result)
elif isinstance(result, str):
print(result)
elif isinstance(result, bool):
if result is True:
print("true")
else:
print("false")
def walkTree(self, node):
if isinstance(node, int):
return node
if isinstance(node, str):
return node
if node is None:
return None
if node[0] == 'say':
return self.walkTree(node[1])
if node[0] == 'num':
return node[1]
if node[0] == 'str':
return node[1]
if node[0] == 'eq':
return self.walkTree(node[1]) == self.walkTree(node[2])
if node[0] == 'add':
return self.walkTree(node[1]) + self.walkTree(node[2])
elif node[0] == 'sub':
return self.walkTree(node[1]) - self.walkTree(node[2])
elif node[0] == 'mul':
return self.walkTree(node[1]) * self.walkTree(node[2])
elif node[0] == 'div':
return self.walkTree(node[1]) / self.walkTree(node[2])
if node[0] == 'var_assign':
self.variables[node[1]] = self.walkTree(node[2])
if node[0] == 'var':
try:
return self.variables[node[1]]
except LookupError:
print("Undefined name '{}'".format(node[1]))
return 0
This:
if __name__ == '__main__':
lexer = NoobpyLexer()
parser = NoobpyParser()
variables = {}
args = argparse.ArgumentParser()
args.add_argument(metavar='filename', dest="filename", type=str, help='name of the file you want to run')
args = args.parse_args()
with open(args.filename) as file:
for line in file.readlines():
tree = parser.parse(lexer.tokenize(line))
NoobpyExecute(tree, variables)
Example test.noob
x = 2
x
^ prints 2