from Tokenizer import tokenize, TokenType from Parser import parse from AST import * from Note import Note from Error import RuntimeException def evaluateProgram(program, environment): for node in program.children: evaluate(node, environment) def evaluateInteger(integer, environment): return integer.value def evaluatePercent(percent, environment): return percent.value.value * 0.01 def evaluateIdentifier(identifier, environment): value = environment.findVariable(identifier.identifier) return value def evaluateString(string, environment): value = string.value for scope in reversed(environment.scopes): for k, v in scope.items(): value = value.replace('{' + k + '}', objectString(v)) #TODO: poprawic return value def objectString(obj): if isinstance(obj, str): return obj if isinstance(obj, int): return str(obj) if isinstance(obj, Note): return obj.note.name if isinstance(obj, list): return "(" + ", ".join([objectString(v) for v in obj]) + ")" if isinstance(obj, float): return f"{int(obj*100)}%" if obj is None: raise RuntimeException(None, f"Trying to interpret void") raise RuntimeException(None, f"Don't know how to interpret {str(obj)}") def evaluateNote(note, environment): return note.value def evaluateFunctionDefinition(definition, environment): name = definition.name params = list([p for p in definition.parameters.children if not isinstance(p, CommaNode)]) body = definition.body.children if not isinstance(definition.parent, Program): raise RuntimeException(name.pos, f"Functions can be defined only on the top level of script") for p in params: if not isinstance(p, IdentifierNode): raise RuntimeException(p.pos, "Parameter of function definition must be an identifier") if name.identifier in environment.customFunctions or name.identifier in environment.functions: raise RuntimeException(name.pos, f"Function '{name.identifier}' already exists") environment.customFunctions[name.identifier] = { 'params': params, 'body': body } def evaluateFunctionCall(functionCall, environment): funcName = functionCall.identifier.identifier arguments = evaluateList(functionCall.arguments, environment) for name, function in environment.customFunctions.items(): if funcName == name: if len(function['params']) != len(arguments): raise RuntimeException(functionCall.pos, f"Calling '{funcName}' requires {len(function['params'])} and {len(arguments)} was passed") environment.scopes.append({ function['params'][i].identifier: v for i, v in enumerate(arguments) }) returnValue = None for node in function['body']: if not isinstance(node, ReturnNode): evaluate(node, environment) else: returnValue = evaluateReturn(node, environment) environment.scopes.pop(-1) return returnValue for name, definition in environment.functions.items(): if name == funcName: return definition(arguments, environment) raise RuntimeException(functionCall.pos, f"Function '{funcName}' does not exist") def evaluateReturn(returnNode, environment): return evaluate(returnNode.value, environment) def evaluateComma(comma, environment): pass def evaluateBlock(block, environment): environment.scopes.append({}) for node in block.children: evaluate(node, environment) environment.scopes.pop(-1) def evaluateList(list, environment): return [evaluate(e, environment) for e in list if not isinstance(e, CommaNode)] def evaluateAssignment(assignment, environment): target = assignment.target.identifier value = evaluate(assignment.value, environment) scopeOfExistingVariable = environment.findVariableScope(target) if scopeOfExistingVariable is not None: scopeOfExistingVariable[target] = value else: environment.scopes[-1][target] = value def evaluateAsterisk(asterisk, environment): count = evaluate(asterisk.iterator, environment) if isinstance(count, int): for i in range(count): if isinstance(asterisk.iterator, IdentifierNode): environment.scopes[-1][f"_{asterisk.iterator.identifier}"] = i+1 else: environment.scopes[-1]["_"] = i+1 evaluate(asterisk.statement, environment) if isinstance(asterisk.iterator, IdentifierNode): del environment.scopes[-1][f"_{asterisk.iterator.identifier}"] else: environment.scopes[-1]["_"] = i+1 elif isinstance(count, list): for i, v in enumerate(count): if isinstance(asterisk.iterator, IdentifierNode): environment.scopes[-1][f"_{asterisk.iterator.identifier}"] = i+1 environment.scopes[-1][f"{asterisk.iterator.identifier}_"] = v else: environment.scopes[-1]["_"] = i+1 environment.scopes[-1]["__"] = v evaluate(asterisk.statement, environment) if isinstance(asterisk.iterator, IdentifierNode): del environment.scopes[-1][f"_{asterisk.iterator.identifier}"] del environment.scopes[-1][f"{asterisk.iterator.identifier}_"] else: del environment.scopes[-1]["_"] del environment.scopes[-1]["__"] def evaluateColon(colon, environment): if isinstance(colon.a, NoteLiteralNode) and isinstance(colon.b, NoteLiteralNode): return Note.range(colon.a.value, colon.b.value) elif isinstance(colon.a, IntegerLiteralNode) and isinstance(colon.b, IntegerLiteralNode): return list(range(colon.a.value, colon.b.value+1)) raise RuntimeException(colon.pos, "Invalid colon arguments") def evaluate(input, environment): if isinstance(input, Program): return evaluateProgram(input, environment) if isinstance(input, IntegerLiteralNode): return evaluateInteger(input, environment) if isinstance(input, PercentNode): return evaluatePercent(input, environment) if isinstance(input, StringLiteralNode): return evaluateString(input, environment) if isinstance(input, NoteLiteralNode): return evaluateNote(input, environment) if isinstance(input, FunctionDefinitionNode): return evaluateFunctionDefinition(input, environment) if isinstance(input, FunctionCallNode): return evaluateFunctionCall(input, environment) if isinstance(input, CommaNode): return evaluateComma(input, environment) if isinstance(input, BlockNode): return evaluateBlock(input, environment) if isinstance(input, ListNode): return evaluateList(input, environment) if isinstance(input, AssignExpression): return evaluateAssignment(input, environment) if isinstance(input, AsteriskStatementNode): return evaluateAsterisk(input, environment) if isinstance(input, ColonNode): return evaluateColon(input, environment) if isinstance(input, IdentifierNode): return evaluateIdentifier(input, environment)