Skip to content

Commit

Permalink
first draft for parser/grammar
Browse files Browse the repository at this point in the history
  • Loading branch information
andreasWallner committed Oct 2, 2014
1 parent 2ac1d6a commit d906750
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 3 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ Grammar
identifier ::= [a-zA-Z_]+
value ::= identifier
string ::= '"' [^"]+ '"' | [a-zA-Z0-9]+
number ::= [0-9]+
natural ::= [0-9]+
float ::= number ('.' number)? | '.' number
parameter ::= '{' sequence '}' | string
instruction ::= float? symbol ( '(' parameter ( ',' parameter )? ')')?
parameters ::= parameter ( "," parameter )*
instruction ::= float? symbol ( '(' parameters ')')?
| '[' ( indentifier '=' )? value ']'
sequence ::= float? '{' instruction+ '}' | instruction+
sequence ::= number? '{' instruction+ '}' | instruction+

TODO
====
Expand Down
90 changes: 90 additions & 0 deletions waveshaper/Parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from parsimonious.grammar import Grammar
from utils import Trace
from instructions import get_instruction
from InstructionSequence import InstructionSequence
from EnvironmentChangeInstruction import EnvironmentChangeInstruction

class Wave(object):
def __init__(self):
pass

def parse(self, source, root=None):
if root is None:
root = 'wave'

grammar = '\n'.join(v.__doc__ for k, v in vars(self.__class__).items()
if '__' not in k and hasattr(v, '__doc__') and v.__doc__)

return Grammar(grammar)[root].parse(source)

def eval(self, source, root=None):
node = self.parse(source, root) if isinstance(source, str) else source
method = getattr(self, node.expr_name, lambda node, children: children)
return method(node, [self.eval(n) for n in node])

def wave(self, node, children):
'wave = sequence'
return children

def symbol(self, node, children):
'symbol = ~"[a-zA-Z]"'
return node.text

def identifier(self, node, children):
'identifier = ~"[a-zA-Z_]+"'
return node.text

def string(self, node, children):
'string = ( ~"[a-zA-Z0-9]+" ) / ( "\\"" ~"[^\\"]*" "\\"" )'
text = node.text if node.text[0] != '"' else node.text[1:-1]
return text

def sequence(self, node, children):
'sequence = ( natural? "{" instruction + "}" ) / instruction+'
instr = children[0]
if len(instr) < 3 or node.children[0].children[1].text != '{': # just instruction+
return InstructionSequence(instr, 1)
else:
count, _, seq, _ = instr
count = 1 if not count else count[0]
return InstructionSequence(seq, count)

def parameter(self, node, children):
'parameter = string / ( sequence )'
return children[0]

def parameters(self, node, children):
'parameters = parameter ( "," parameter )*'
par = [children[0]] + ([x[1] for x in children[1]])
return par

def ec_instruction(self, node, children):
'ec_instruction = "[" ( identifier "=" )? string "]"'
identifier = None if not children[1] else children[1][0][0]
return EnvironmentChangeInstruction(identifier, children[2])

def rndr_instruction(self, node, children):
'rndr_instruction = float? symbol ( "(" parameters ")" )?'

width, sym, par = children

width = None if not width else width[0]
par = None if not par else par[0][1]

return get_instruction(sym, width, par)

def instruction(self, node, children):
'instruction = rndr_instruction / ec_instruction'
return children[0]

def natural(self, node, children):
'natural = ~"[0-9]+"'
return int(node.text)

def float(self, node, children):
'float = ( natural ( "." natural )? ) / ( "." natural )'
return float(node.text)

def _(self, node, children):
'_ = ~"\s*"'
pass
13 changes: 13 additions & 0 deletions waveshaper/instructions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from RenderInstruction import RenderInstruction
from BackgroundInstruction import BackgroundInstruction
from utils import Trace

def get_instruction(symbol, width = None, parameters = None):
if width is None:
width = 1

if symbol == 'B':
return BackgroundInstruction(parameters[0])
else:
param = None if not parameters else parameters[0]
return RenderInstruction(symbol, width, param)
104 changes: 104 additions & 0 deletions waveshaper/test_Parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import unittest
from Parser import Wave
from RenderInstruction import RenderInstruction
from BackgroundInstruction import BackgroundInstruction
from InstructionSequence import InstructionSequence
from EnvironmentChangeInstruction import EnvironmentChangeInstruction

class tests(unittest.TestCase):
def test_minimal(self):
self.assertEqual(
Wave().eval('L'),
InstructionSequence([RenderInstruction('L', 1)], 1))

def test_width(self):
self.assertEqual(
Wave().eval('3L'),
InstructionSequence([RenderInstruction('L', 3)], 1))

def test_param(self):
self.assertEqual(
Wave().eval('D(0xAA)'),
InstructionSequence([RenderInstruction('D', 1, '0xAA')], 1))

def test_multiple_params(self):
self.assertEqual(
Wave().eval('3L(x,x,x)'),
InstructionSequence([RenderInstruction('L', 3, 'x')], 1))

def test_multiple_instr(self):
self.assertEqual(
Wave().eval('LLL'),
InstructionSequence(3*[RenderInstruction('L', 1)], 1))

def test_explicit_sequence(self):
self.assertEqual(
Wave().eval('{LLL}'),
InstructionSequence(3*[RenderInstruction('L', 1)], 1))

def test_mutiple_sequence(self):
self.assertEqual(
Wave().eval('4{LLL}'),
InstructionSequence(3*[RenderInstruction('L', 1)], 4))

def test_sequence_param(self):
self.assertEqual(
Wave().eval('B({LL})'),
InstructionSequence([BackgroundInstruction(InstructionSequence(2*[RenderInstruction('L', 1)]))]))

def test_ec_instruction(self):
self.assertEqual(
Wave().eval('[foo=bar]'),
InstructionSequence([EnvironmentChangeInstruction('foo', 'bar')]))

def test_ec_no_indentifier(self):
self.assertEqual(
Wave().eval('[bar]'),
InstructionSequence([EnvironmentChangeInstruction('color', 'bar')]))

def test_complete(self):
self.assertEqual(
Wave().eval('LH4LB({[grey]H0L})LL(0)'),
InstructionSequence([
RenderInstruction('L', 1),
RenderInstruction('H', 1),
RenderInstruction('L', 4),
BackgroundInstruction(
InstructionSequence([
EnvironmentChangeInstruction('color', 'grey'),
RenderInstruction('H', 1),
RenderInstruction('L', 0)])),
RenderInstruction('L', 1),
RenderInstruction('L', 1, '0') ]))

def test_identifier(self):
self.assertEqual(
Wave().eval('_asdf', 'identifier'),
'_asdf')

def test_natural(self):
self.assertEqual(
Wave().eval('123', 'natural'),
123)

def test_float(self):
self.assertEqual(
Wave().eval('123', 'float'),
123)

self.assertEqual(
Wave().eval('1.2', 'float'),
1.2)

self.assertEqual(
Wave().eval('.4', 'float'),
0.4)

def test_string(self):
self.assertEqual(
Wave().eval('asdf', 'string'),
'asdf')

self.assertEqual(
Wave().eval('"asdf,xy"', 'string'),
'asdf,xy')

0 comments on commit d906750

Please sign in to comment.