Create note package

This commit is contained in:
Bartłomiej Pluta
2019-07-03 10:16:05 +02:00
parent f826516d8f
commit 2823fd1896
40 changed files with 188 additions and 958 deletions

View File

@@ -1,223 +0,0 @@
from enum import Enum
from Note import Note
class Node:
def __init__(self, parent, pos):
self.children = []
self.parent = parent
self.pos = pos
for child in self.children:
child.parent = self
def __repr__(self):
return self.__str__()
def __len__(self):
return len(self.children)
def __getitem__(self, index):
return self.children[index]
def append(self, node):
node.parent = self
self.children.append(node)
def pop(self, index):
return self.children.pop(index)
def _print(self, level):
string = f"{pad(level)}{self.__class__.__name__}({self.parent.__class__.__name__}):\n"
for child in self.children:
if isinstance(child, str) or isinstance(child, int) or isinstance(child, Note):
string += pad(level+1) + f"'{child}'\n"
else:
string += child._print(level+1)
return string
def __str__(self):
return self._print(0)
def pad(level):
return (" " * level)
class Program(Node):
def __init__(self):
Node.__init__(self, None, (-1, -1))
#def __str__(self):
#return "Program:\n" + "\n".join([str(e) for e in self.children])
def print(self):
print(self._print(0))
class BlockNode(Node):
def __init__(self, parent, pos):
Node.__init__(self, parent, pos)
#def __str__(self):
#return "B{\n" + "\n".join([str(e) for e in self.children]) + "\n}"
class BlockItemNode(Node):
def __init__(self, statement, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(statement)
self.statement = self.children[0]
class CloseBlockNode(Node):
def __init__(self, parent, pos):
Node.__init__(self, parent, pos)
class ListNode(Node):
def __init__(self, parent, pos):
Node.__init__(self, parent, pos)
#def __str__(self):
#return "@(" + ", ".join([str(e) for e in self.children]) + ")"
class IdentifierNode(Node):
def __init__(self, identifier, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(identifier)
self.identifier = self.children[0]
#def __str__(self):
#return f"L'{self.identifier}'"
class AssignmentNode(Node):
def __init__(self, target, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.extend([target, value])
self.target = self.children[0]
self.value = self.children[1]
#def __str__(self):
#return f"A[{self.target} = {self.value}]"
class AsteriskNode(Node):
def __init__(self, iterator, statement, parent, pos):
Node.__init__(self, parent, pos)
self.children.extend([iterator, statement])
self.iterator = self.children[0]
self.statement = self.children[1]
#def __str__(self):
#return f"*({self.iterator}: {self.statement})"
class ColonNode(Node):
def __init__(self, a, b, parent, pos):
Node.__init__(self, parent, pos)
self.children.extend([a, b])
self.a = self.children[0]
self.b = self.children[1]
#def __str__(self):
#return f":({self.a}, {self.b})"
class ExpressionNode(Node):
def __init__(self, parent, pos):
Node.__init__(self, parent, pos)
#def __str__(self):
#return f"{self.__class__.__name__}('{self.value}')"
class IntegerLiteralNode(ExpressionNode):
def __init__(self, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(value)
self.value = self.children[0]
#def __str__(self):
#return f"i'{self.value}'"
class StringLiteralNode(ExpressionNode):
def __init__(self, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(value)
self.value = self.children[0]
#def __str__(self):
#return f"s'{self.value}'"
class NoteLiteralNode(ExpressionNode):
def __init__(self, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(value)
self.value = self.children[0]
#def __str__(self):
#return f"n'{self.value.note}[{self.value.octave}, {self.value.duration}]'"
class FunctionCallNode(Node):
def __init__(self, identifier, arguments, parent, pos):
Node.__init__(self, parent, pos)
self.children.extend([identifier, arguments])
self.identifier = self.children[0]
self.arguments = self.children[1]
#def __str__(self):
#return f"F({self.identifier}: {self.arguments})"
class CommaNode(Node):
def __init__(self, parent, pos):
Node.__init__(self, parent, pos)
#def __str__(self):
#return "[,]"
class PercentNode(Node):
def __init__(self, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(value)
self.value = self.children[0]
#def __str__(self):
#return f"%'{self.value}'"
class FunctionDefinitionNode(Node):
def __init__(self, name, parameters, body, parent, pos):
Node.__init__(self, parent, pos)
self.children.extend([name, parameters, body])
self.name = self.children[0]
self.parameters = self.children[1]
self.body = self.children[2]
#def __str__(self):
#return f"$F'{self.name}{self.parameters}{self.body}"
class ReturnNode(Node):
def __init__(self, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(value)
self.value = self.children[0]
#def __str__(self):
#return f"Ret({self.value})"
class ListItemNode(Node):
def __init__(self, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(value)
self.value = self.children[0]
class AccessNode(Node):
def __init__(self, element, property, parent, pos):
Node.__init__(self, parent, pos)
self.children.extend([element, property])
self.element = self.children[0]
self.property = self.children[1]
class CloseListNode(Node):
pass

View File

@@ -1,128 +0,0 @@
from enum import Enum
import math
from Error import SyntaxException
class NotePitch(Enum):
C = 0
CIS = 1
D = 2
DIS = 3
E = 4
F = 5
FIS = 6
G = 7
GIS = 8
A = 9
AIS = 10
H = 11
def __str__(self):
return self.name
def __repr__(self):
return self.__str__()
def toFrequency(self):
return {
NotePitch.C: 16.35,
NotePitch.CIS: 17.32,
NotePitch.D: 18.35,
NotePitch.DIS: 19.45,
NotePitch.E: 20.60,
NotePitch.F: 21.83,
NotePitch.FIS: 23.12,
NotePitch.G: 24.50,
NotePitch.GIS: 25.96,
NotePitch.A: 27.50,
NotePitch.AIS: 29.17,
NotePitch.H: 30.87
}[self]
@staticmethod
def checkInterval(a, b):
return a.value - b.value
@staticmethod
def toPitch(string):
try:
map = { 'c': NotePitch.C, 'c#': NotePitch.CIS, 'db': NotePitch.CIS, 'd': NotePitch.D,
'd#': NotePitch.DIS, 'eb': NotePitch.DIS, 'e': NotePitch.E, 'fb': NotePitch.E, 'e#': NotePitch.F,
'f': NotePitch.F, 'f#': NotePitch.FIS, 'gb': NotePitch.FIS, 'g': NotePitch.G, 'g#': NotePitch.GIS,
'ab': NotePitch.GIS, 'a': NotePitch.A, 'a#': NotePitch.AIS, 'b': NotePitch.AIS, 'h': NotePitch.H
}
return map[string.lower()]
except KeyError as e:
raise SyntaxException(None, f"Note '{string}' does not exist")
class Note:
def __init__(self, note, octave = 4, duration = 4, dot = False):
if type(note) == str:
self.note = NotePitch.toPitch(note)
else:
self.note = note
self.octave = octave
self.duration = duration
self.dot = dot
def toFrequency(self):
return self.note.toFrequency() * 2 ** self.octave
def transpose(self, interval):
origIntRepr = self._intRepr()
transposedIntRepr = origIntRepr + interval
return Note._fromIntRepr(transposedIntRepr, self.duration, self.dot)
def withDuration(self, duration):
return Note(self.note, self.octave, duration, self.dot)
def withOctave(self, octave):
return Note(self.note, octave, self.duration, self.dot)
def withDot(self):
return Note(self.note, self.octave, self.duration, True)
def withoutDot(self):
return Note(self.note, self.octave, self.duration, False)
def _intRepr(self):
return self.octave * len(NotePitch) + self.note.value
def __str__(self):
return f"{self.note}({self.octave}')[{self.duration}{'.' if self.dot else ''}]"
def __repr__(self):
return self.__str__()
@staticmethod
def _fromIntRepr(intRepr, duration = 4, dot = False):
note = NotePitch(intRepr % len(NotePitch))
octave = int(intRepr / len(NotePitch))
return Note(note, octave, duration, dot)
@staticmethod
def checkInterval(a, b):
return b._intRepr() - a._intRepr()
@staticmethod
def range(a, b):
return [Note._fromIntRepr(x) for x in range(a._intRepr(), b._intRepr()+1)]
def intervalToString(interval):
octaveInterval = int(abs(interval) / len(NotePitch))
pitchInterval = abs(interval) % len(NotePitch)
pitchIntervalName = {
0: "1",
1: "2m",
2: "2M",
3: "3m",
4: "3M",
5: "4",
6: "5d/4A",
7: "5",
8: "6m",
9: "6M",
10: "7m",
11: "7M"
}
return (str(pitchIntervalName[pitchInterval]) + (f"(+{octaveInterval}')" if octaveInterval > 0 else ""))

View File

@@ -1,230 +0,0 @@
from Tokenizer import *
from Note import *
from AST import *
from Error import SyntaxException
def expectedFound(expected, found):
raise SyntaxException(None, f"Expected: {expected}, found: {found}")
def assertType(expected, found):
if expected != found:
raise SyntaxException(None, f"Expected: {expected}, found: {found}")
def parseInteger(input, parent):
token = input.pop(0)
return IntegerLiteralNode(int(token.value), parent, token.pos)
def parseString(input, parent):
token = input.pop(0)
return StringLiteralNode(token.value[1:-1], parent, token.pos)
def parseNote(input, parent):
token = input.pop(0)
value = token.value
consumedChars = 1
notePitch = value[consumedChars]
consumedChars += 1
octave = 4
duration = 4
dot = False
if consumedChars < len(value) and value[consumedChars] in ('b', '#'):
notePitch += value[consumedChars]
consumedChars += 1
if consumedChars < len(value) and re.match(r'\d', value[consumedChars]):
octave = int(value[consumedChars])
consumedChars += 1
if consumedChars < len(value) and value[consumedChars] == '.':
consumedChars += 1
durationString = ''
while consumedChars < len(value) and re.match(r'\d', value[consumedChars]):
durationString += value[consumedChars]
consumedChars += 1
duration = int(durationString)
if consumedChars < len(value) and value[consumedChars] == '.':
dot = True
consumedChars += 1
return NoteLiteralNode(Note(notePitch, octave, duration, dot), parent, token.pos)
def parseComma(input, parent):
token = input.pop(0)
return CommaNode(parent, token.pos)
def parseList(input, parent):
token = input.pop(0)
node = ListNode(parent, token.pos)
while input[0].type != TokenType.CLOSE_PAREN:
element = parseArrayElement(input, node)
if element is None:
raise SyntaxException(input[0].pos, "Invalid element '{input[0].value}'")
node.append(element)
if input[0].type != TokenType.CLOSE_PAREN:
expectedFound(TokenType.CLOSE_PAREN, input[0].type)
input.pop(0)
return node
def parseBlock(input, parent):
token = input.pop(0)
block = BlockNode(parent, token.pos)
while input[0].type != TokenType.CLOSE_BRACKET:
block.append(parseToken(input, block))
if input[0].type != TokenType.CLOSE_BRACKET:
expectedFound(TokenType.CLOSE_BRACKET, input[0].type)
input.pop(0)
return block
def parseAsterisk(input, parent):
token = input.pop(0)
iterator = parent.pop(-1)
value = parseStatement(input, parent)
asterisk = AsteriskStatementNode(iterator, value, parent, token.pos)
iterator.parent = asterisk
value.parent = asterisk
return asterisk
def parseNoteOrColon(input, parent):
note = parseNote(input, parent)
if len(input) > 1 and input[0].type == TokenType.COLON:
token = input.pop(0)
b = parseNote(input, parent)
if b is None:
raise SyntaxException(input[0].pos, f"Invalid colon argument '{input[0].value}'")
colon = ColonNode(note, b, parent, token.pos)
note.parent = colon
b.parent = colon
return colon
return note
def parseIntegerOrColonOrPercent(input, parent):
integer = parseInteger(input, parent)
if len(input) > 1 and input[0].type == TokenType.COLON:
token = input.pop(0)
b = parseInteger(input, parent)
if b is None:
raise SyntaxException(input[0].pos, f"Invalid colon argument '{input[0].value}'")
colon = ColonNode(integer, b, parent, token.pos)
integer.parent = colon
b.parent = colon
return colon
if len(input) > 0 and input[0].type == TokenType.PERCENT:
input.pop(0)
percent = PercentNode(integer, parent, integer.pos)
integer.parent = percent
return percent
return integer
def parseFunctionCallOrAssignOrIdentifier(input, parent):
token = input.pop(0)
identifier = IdentifierNode(token.value, parent, token.pos)
# Function call
if len(input) > 0 and input[0].type == TokenType.OPEN_PAREN:
arguments = parseList(input, parent)
func = FunctionCallNode(identifier, arguments, parent, token.pos)
identifier.parent = func
arguments.parent = func
return func
# Assign
if len(input) > 1 and input[0].type == TokenType.ASSIGN:
token = input.pop(0)
value = parseExpression(input, parent) #
assign = AssignExpression(identifier, value, parent, token.pos)
identifier.parent = assign
value.parent = assign
return assign
return identifier
def parseMinus(input, parent):
token = input.pop(0)
value = parseInteger(input, parent)
return IntegerLiteralNode(-value.value, parent, token.pos)
def parseFunctionDefinition(input, parent):
input.pop(0)
assertType(TokenType.IDENTIFIER, input[0].type)
token = input.pop(0)
name = IdentifierNode(token.value, parent, token.pos)
assertType(TokenType.OPEN_PAREN, input[0].type)
parameters = parseList(input, parent)
assertType(TokenType.OPEN_BRACKET, input[0].type)
body = parseBlock(input, parent)
func = FunctionDefinitionNode(name, parameters, body, parent, token.pos)
name.parent = func
parameters.parent = func
body.parent = func
return func
def parseReturn(input, parent):
token = input.pop(0)
value = parseExpression(input, parent)
returnNode = ReturnNode(value, parent, token.pos)
value.parent = returnNode
return returnNode
def parseExpression(input, parent):
type = input[0].type
if type == TokenType.FUNCTION:
return parseFunctionDefinition(input, parent)
if type == TokenType.RETURN:
return parseReturn(input, parent)
if type == TokenType.MINUS:
return parseMinus(input, parent)
if type == TokenType.INTEGER:
return parseIntegerOrColonOrPercent(input, parent)
if type == TokenType.STRING:
return parseString(input, parent)
if type == TokenType.NOTE:
return parseNoteOrColon(input, parent)
if type == TokenType.IDENTIFIER:
return parseFunctionCallOrAssignOrIdentifier(input, parent)
if type == TokenType.OPEN_PAREN:
return parseList(input, parent)
raise SyntaxException(input[0].pos, f"Unexpected character '{input[0].value}'")
def parseArrayElement(input, parent):
type = input[0].type
if type == TokenType.COMMA:
return parseComma(input, parent)
return parseExpression(input, parent)
def parseStatement(input, parent):
type = input[0].type
if type == TokenType.OPEN_BRACKET:
return parseBlock(input, parent)
if type == TokenType.ASTERISK:
return parseAsterisk(input, parent)
return parseExpression(input, parent)
def parseToken(input, parent):
#import pdb; pdb.set_trace()
return parseStatement(input, parent)
def parse(input):
root = Program()
while len(input) > 0:
root.append(parseToken(input, root))
return root

View File

@@ -1,377 +0,0 @@
from AST import *
from Tokenizer import TokenType
from Error import SyntaxException
from Note import Note, NotePitch
import re
def assertToken(expected, input):
if not input.hasCurrent():
raise SyntaxException(None, f"Expected '{expected}'")
if expected != input.current().type:
raise SyntaxException(input.current().pos, f"Expected '{expected}', found '{input.current().value}'")
def runParsers(input, parent, parsers):
for parser in parsers:
value = parser(input, parent)
if value is not None:
return value
return None
def returnAndGoAhead(input, getValue):
value = getValue(input.current())
input.ahead()
return value
# int -> INTEGER
def parseInteger(input, parent):
if input.current().type == TokenType.INTEGER:
integer = IntegerLiteralNode(int(input.current().value), parent, input.current().pos)
input.ahead()
return integer
return None
# percent -> int '%'
def parseIntegerAndPercent(input, parent):
integer = parseInteger(input, parent)
if integer is not None and input.hasCurrent() and input.current().type == TokenType.PERCENT:
percent = PercentNode(integer, parent, input.current().pos)
integer.parent = percent
input.ahead()
return percent
return integer
# string -> STRING
def parseString(input, parent):
if input.current().type == TokenType.STRING:
string = StringLiteralNode(input.current().value[1:len(input.current().value)-1], parent, input.current().pos)
input.ahead()
return string
return None
# id -> IDENTIFIER
def parseIdentifier(input, parent):
if input.current().type == TokenType.IDENTIFIER:
identifier = IdentifierNode(input.current().value, parent, input.current().pos)
input.ahead()
return identifier
return None
def parseIdentifierOrFunctionCallOrAssignment(input, parent):
identifier = parseIdentifier(input, parent)
if identifier is not None and input.hasCurrent():
if input.current().type == TokenType.ASSIGN:
token = input.current()
input.ahead()
expr = parseExpression(input, parent)
assignment = AssignmentNode(identifier, expr, parent, token.pos)
identifier.parent = assignment
expr.parent = assignment
return assignment
args = parseList(input, parent)
if args is not None:
functionCall = FunctionCallNode(identifier, args, parent, identifier.pos)
args.parent = functionCall
identifier.parent = functionCall
return functionCall
return identifier
# note -> NOTE
def parseNote(input, parent):
if input.current().type == TokenType.NOTE:
token = input.current()
value = token.value
consumedChars = 1
notePitch = value[consumedChars]
consumedChars += 1
octave = 4
duration = 4
dot = False
if consumedChars < len(value) and value[consumedChars] in ('b', '#'):
notePitch += value[consumedChars]
consumedChars += 1
if consumedChars < len(value) and re.match(r'\d', value[consumedChars]):
octave = int(value[consumedChars])
consumedChars += 1
if consumedChars < len(value) and value[consumedChars] == '.':
consumedChars += 1
durationString = ''
while consumedChars < len(value) and re.match(r'\d', value[consumedChars]):
durationString += value[consumedChars]
consumedChars += 1
duration = int(durationString)
if consumedChars < len(value) and value[consumedChars] == 'd':
dot = True
consumedChars += 1
input.ahead()
return NoteLiteralNode(Note(notePitch, octave, duration, dot), parent, token.pos)
return None
# list -> CLOSE_PAREN | expr listTail
def parseList(input, parent):
if input.current().type == TokenType.OPEN_PAREN:
node = ListNode(parent, input.current().pos)
input.ahead()
# list -> CLOSE_PAREN (end of list)
if input.hasCurrent() and input.current().type == TokenType.CLOSE_PAREN:
close = CloseListNode(node, input.current().pos)
node.append(close)
input.ahead()
return node
# list -> expr listTail
if input.hasCurrent():
token = input.current()
expr = parseExpression(input, node)
item = ListItemNode(expr, node, token.pos)
expr.parent = item
node.append(item)
listTail = parseListTail(input, item)
item.append(listTail)
return node
return None
# listTail -> COMMA expr listTail | CLOSE_PAREN
def parseListTail(input, parent):
# listTail -> CLOSE_PAREN
if input.hasCurrent() and input.current().type == TokenType.CLOSE_PAREN:
close = CloseListNode(parent, input.current().pos)
input.ahead()
return close
# listTail -> COMMA expr listTail
if input.hasCurrent() and input.hasMore():
assertToken(TokenType.COMMA, input)
input.ahead()
expr = rollup(parseExpression)(input, parent)
if expr is not None:
item = ListItemNode(expr, parent, expr.pos)
expr.parent = item
listTail = parseListTail(input, item)
item.append(listTail)
listTail.parent = item
return item
return None
# colon -> expr ':' expr
def parseColon(input, parent):
if input.hasMore() and input.current().type == TokenType.COLON:
expr1 = parent.pop(-1)
token = input.current()
input.ahead()
expr2 = parseExpression(input, parent)
colon = ColonNode(expr1, expr2, parent, token.pos)
expr1.parent = colon
expr2.parent = colon
return colon
return None
# minus -> '-' int
def parseMinus(input, parent):
if input.current().type == TokenType.MINUS:
token = input.current()
input.ahead()
expr = parseInteger(input, parent)
return IntegerLiteralNode(-expr.value, parent, token.pos)
# block -> '{' '}' | '{' blockItem
def parseBlock(input, parent):
# '{'
if input.current().type == TokenType.OPEN_BRACKET:
token = input.current()
input.ahead()
node = BlockNode(parent, token.pos)
# '}'
if input.hasCurrent() and input.current().type == TokenType.CLOSE_BRACKET:
input.ahead()
return node
# blockItem
if input.hasCurrent():
item = parseBlockItem(input, node)
node.append(item)
return node
return None
# blockItem -> stmt | '}'
def parseBlockItem(input, parent):
# '}'
if input.hasCurrent() and input.current().type == TokenType.CLOSE_BRACKET:
close = CloseBlockNode(parent, input.current().pos)
input.ahead()
return close
if input.hasCurrent():
stmt = parseStatement(input, parent)
if stmt is not None:
item = BlockItemNode(stmt, parent, stmt.pos)
stmt.parent = item
nextBlockItem = parseBlockItem(input, item)
if nextBlockItem != None:
item.append(nextBlockItem)
return item
return None
def rollup(parser):
def _rollup(input, parent):
node = Node(None, (-1, -1))
elem = parser(input, node)
while elem is not None:
node.append(elem)
elem = parser(input, node)
return node.children[0] if len(node.children) > 0 else None
return _rollup
def parseFunctionDefinition(input, parent):
if input.current().type == TokenType.FUNCTION:
token = input.current()
input.ahead()
assertToken(TokenType.IDENTIFIER, input)
identifier = parseIdentifier(input, parent)
assertToken(TokenType.OPEN_PAREN, input)
args = parseList(input, parent)
assertToken(TokenType.OPEN_BRACKET, input)
body = parseBlock(input, parent)
function = FunctionDefinitionNode(identifier, args, body, parent, token.pos)
identifier.parent = function
args.parent = function
body.parent = function
return function
return None
def parseReturn(input, parent):
if input.current().type == TokenType.RETURN:
token = input.current()
input.ahead()
expr = parseExpression(input, parent)
node = ReturnNode(expr, parent, token.pos)
expr.parent = node
return node
return None
# access -> expr '.' expr
#TODO: dodać dziedziczenie wszystkich expressions po jednym typie ExpressionNode
# i potem sprawdzać przy wszystkich parent.pop(-1) czy pobrany z parenta element
# jest rzeczywiście wyrażeniem, bo teraz możliwe jest np. {}.fun()
def parseAccess(input, parent):
if input.current().type == TokenType.DOT:
token = input.current()
input.ahead()
element = parent.pop(-1)
property = parseExpression(input, parent)
node = AccessNode(element, property, parent, token.pos)
element.parent = node
property.parent = node
return node
return None
def parseStatement(input, parent):
stmt = runParsers(input, parent, [
parseBlock,
parseExpression,
parseFunctionDefinition,
parseReturn
])
asterisk = parseAsterisk(stmt, input, parent)
if asterisk is not None:
return asterisk
return stmt
# asterisk -> expr '*' stmt
def parseAsterisk(expr, input, parent):
if input.hasMore() and input.current().type == TokenType.ASTERISK:
token = input.current()
input.ahead()
stmt = parseStatement(input, parent)
asterisk = AsteriskNode(expr, stmt, parent, token.pos)
expr.parent = asterisk
stmt.parent = asterisk
return asterisk
return None
def parseExpression(input, parent):
expr = runParsers(input, parent, [
parseIntegerAndPercent,
parseMinus,
parseString,
parseNote,
parseList,
parseIdentifierOrFunctionCallOrAssignment,
parseAccess,
])
colon = parseColon(expr, input, parent)
if colon is not None:
return colon
return expr
# colon -> expr ':' expr
def parseColon(expr1, input, parent):
if input.hasCurrent() and input.current().type == TokenType.COLON:
token = input.current()
input.ahead()
expr2 = parseExpression(input, parent)
if expr2 is None:
raise SyntaxException(input.current().pos, f"Expected expression '{input.current().value}'")
colon = ColonNode(expr1, expr2, parent, token.pos)
expr1.parent = colon
expr2.parent = colon
return colon
return None
def parseToken(input, parent):
value = runParsers(input, parent, [
parseStatement
])
if value is None:
raise SyntaxException(None, "Unknown statement") #TODO
return value
def parse(input):
root = Program()
while input.hasCurrent():
root.append(parseToken(input, root))
return root

View File

0
smnp/ast/node/access.py Normal file
View File

View File

View File

0
smnp/ast/node/block.py Normal file
View File

0
smnp/ast/node/colon.py Normal file
View File

View File

View File

0
smnp/ast/node/integer.py Normal file
View File

0
smnp/ast/node/list.py Normal file
View File

41
smnp/ast/node/model.py Normal file
View File

@@ -0,0 +1,41 @@
from smnp.note.model import Note
class Node:
def __init__(self, parent, pos):
self.children = []
self.parent = parent
self.pos = pos
for child in self.children:
child.parent = self
def __repr__(self):
return self.__str__()
def __len__(self):
return len(self.children)
def __getitem__(self, index):
return self.children[index]
def append(self, node):
node.parent = self
self.children.append(node)
def pop(self, index):
return self.children.pop(index)
def _print(self, level):
string = f"{pad(level)}{self.__class__.__name__}({self.parent.__class__.__name__}):\n"
for child in self.children:
if isinstance(child, str) or isinstance(child, int) or isinstance(child, Note):
string += pad(level + 1) + f"'{child}'\n"
else:
string += child._print(level + 1)
return string
def __str__(self):
return self._print(0)
def pad(level):
return (" " * level)

0
smnp/ast/node/note.py Normal file
View File

0
smnp/ast/node/percent.py Normal file
View File

0
smnp/ast/node/program.py Normal file
View File

0
smnp/ast/node/ret.py Normal file
View File

0
smnp/ast/node/string.py Normal file
View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

0
smnp/ast/parsers/list.py Normal file
View File

View File

0
smnp/ast/parsers/note.py Normal file
View File

0
smnp/ast/parsers/ret.py Normal file
View File

View File

View File

View File

0
smnp/note/__init__.py Normal file
View File

22
smnp/note/interval.py Normal file
View File

@@ -0,0 +1,22 @@
from smnp.note.pitch import NotePitch
semitonesToIntervalName = {
0: "1",
1: "2m",
2: "2M",
3: "3m",
4: "3M",
5: "4",
6: "5d/4A",
7: "5",
8: "6m",
9: "6M",
10: "7m",
11: "7M"
}
def intervalToString(interval):
octaveInterval = int(abs(interval) / len(NotePitch))
pitchInterval = abs(interval) % len(NotePitch)
return (str(semitonesToIntervalName[pitchInterval]) + (f"(+{octaveInterval}')" if octaveInterval > 0 else ""))

54
smnp/note/model.py Normal file
View File

@@ -0,0 +1,54 @@
from smnp.note.pitch import NotePitch
class Note:
def __init__(self, note, octave = 4, duration = 4, dot = False):
if type(note) == str:
self.note = NotePitch.toPitch(note)
else:
self.note = note
self.octave = octave
self.duration = duration
self.dot = dot
def toFrequency(self):
return self.note.toFrequency() * 2 ** self.octave
def transpose(self, interval):
origIntRepr = self._intRepr()
transposedIntRepr = origIntRepr + interval
return Note._fromIntRepr(transposedIntRepr, self.duration, self.dot)
def withDuration(self, duration):
return Note(self.note, self.octave, duration, self.dot)
def withOctave(self, octave):
return Note(self.note, octave, self.duration, self.dot)
def withDot(self):
return Note(self.note, self.octave, self.duration, True)
def withoutDot(self):
return Note(self.note, self.octave, self.duration, False)
def _intRepr(self):
return self.octave * len(NotePitch) + self.note.value
def __str__(self):
return f"{self.note}({self.octave}')[{self.duration}{'.' if self.dot else ''}]"
def __repr__(self):
return self.__str__()
@staticmethod
def checkInterval(a, b):
return b._intRepr() - a._intRepr()
@staticmethod
def range(a, b):
return [Note._fromIntRepr(x) for x in range(a._intRepr(), b._intRepr()+1)]
@staticmethod
def _fromIntRepr(intRepr, duration = 4, dot = False):
note = NotePitch(intRepr % len(NotePitch))
octave = int(intRepr / len(NotePitch))
return Note(note, octave, duration, dot)

71
smnp/note/pitch.py Normal file
View File

@@ -0,0 +1,71 @@
from enum import Enum
from smnp.error.syntax import SyntaxException
class NotePitch(Enum):
C = 0
CIS = 1
D = 2
DIS = 3
E = 4
F = 5
FIS = 6
G = 7
GIS = 8
A = 9
AIS = 10
H = 11
def toFrequency(self):
return {
NotePitch.C: 16.35,
NotePitch.CIS: 17.32,
NotePitch.D: 18.35,
NotePitch.DIS: 19.45,
NotePitch.E: 20.60,
NotePitch.F: 21.83,
NotePitch.FIS: 23.12,
NotePitch.G: 24.50,
NotePitch.GIS: 25.96,
NotePitch.A: 27.50,
NotePitch.AIS: 29.17,
NotePitch.H: 30.87
}[self]
def __str__(self):
return self.name
def __repr__(self):
return self.__str__()
#@staticmethod
#def checkInterval(a, b):
#return a.value - b.value
@staticmethod
def toPitch(string): #TODO: token zamiast stringa, żeby można było wziąć pos
try:
return stringToPitch[string.lower()]
except KeyError as e:
raise SyntaxException(None, f"Note '{string}' does not exist")
stringToPitch = {
'c': NotePitch.C,
'c#': NotePitch.CIS,
'db': NotePitch.CIS,
'd': NotePitch.D,
'd#': NotePitch.DIS,
'eb': NotePitch.DIS,
'e': NotePitch.E,
'fb': NotePitch.E,
'e#': NotePitch.F,
'f': NotePitch.F,
'f#': NotePitch.FIS,
'gb': NotePitch.FIS,
'g': NotePitch.G,
'g#': NotePitch.GIS,
'ab': NotePitch.GIS,
'a': NotePitch.A,
'a#': NotePitch.AIS,
'b': NotePitch.AIS,
'h': NotePitch.H
}