# -*- Mode: Python; tab-width: 4 -*-

import kjParseBuild
import string

grammar = (
	('form', (
		('exp',							lambda l,c: l[0]),
		('define var = exp',			lambda l,c: ['define', l[1], l[3]]),
		)
	 ),
	
	('exp', (
		('int',							lambda l,c: ['lit', l[0]]),
		('varref',						lambda l,c: l[0]),
		('operator operands',			lambda l,c: ['app', l[0], l[1]]),
		('PROC vars exp',				lambda l,c: ['proc', l[1], l[2]]),
		('IF exp THEN exp ELSE exp',	lambda l,c: ['conditional', l[1], l[3], l[5]]),
		('var := exp',					lambda l,c: ['assign', l[0], l[2]]),
		('exp primop exp',				lambda l,c: ['primop', l[1], l[0], l[2]]),
		('BEGIN sequence END',			lambda l,c: ['sequence', l[1]]),
		('LET decl_list IN exp',		lambda l,c: ['let', l[1], l[3]]),
		('LETRECPROC procdecls IN exp',	lambda l,c: ['letrecproc', l[1], l[3]]),
		)
	 ),

	('operator', (
		('varref',						lambda l,c: l[0]),
		('( exp )',						lambda l,c: l[1]),
		)
	 ),

	('operands', (
		('( )',							lambda l,c: []),
		('( operand_list )',			lambda l,c: l[1]),
		)
	 ),
	
	('operand_list', (
		('operand',						lambda l,c: [l[0]]),
		('operand_list , operand',		lambda l,c: l[0] + [l[2]]),
		)
	 ),
	
	('operand', (
		('exp',							lambda l,c: l[0]),
		)
	 ),

	('varref', (
		('var',							lambda l,c: ['varref', l[0]]),
		)
	 ),

	('vars', (
		('( )',							lambda l,c: []),
		('( varlist )',					lambda l,c: l[1]),
		)
	 ),

	('varlist', (
		('var',							lambda l,c: [l[0]]),
		('varlist , var',				lambda l,c: l[0] + [l[2]]),
		)
	 ),

	('sequence', (
		('exp',							lambda l,c: [l[0]]),
		('sequence ; exp',				lambda l,c: l[0] + [l[2]]),
		)
	 ),

	('decl_list', (
		('decl',						lambda l,c: [l[0]]),
		('decl_list ; decl',			lambda l,c: l[0] + [l[2]]),
		),
	 ),

	('decl', (
		('var = exp',					lambda l,c: ['decl', l[0], l[2]]),
		)
	 ),

	('procdecls', (
		('procdecl',					lambda l,c: [l[0]]),
		('procdecls ; procdecl',		lambda l,c: l[0] + [l[2]]),
		),
	 ),

	('procdecl', (
		('var ( varlist ) = exp',		lambda l,c: ['procdecl', l[0], l[2], l[5]]),
		),
	 ),

	)
	

int_regex = "\(-\)?[0-9]+"
var_regex = "[A-Za-z_+*-][A-Za-z0-9_+*-]*"
prim_regex = "\([*/+>-]\)\|\(==\)\|\(!<\)"

def int_interp (str):
	return string.atoi (str)

def identity (str):
	return str

def translate_grammar (root):
	i = 0
	result = ['%s ::' % root]
	for nt, rules in grammar:
		for rule, action in rules:
			result.append ('@R r%02d :: %s >> %s' % (i, nt, rule))
			i = i + 1
	return string.join (result, '\n')

def bind_rules (g):
	i = 0
	for nt, rules in grammar:
		for rule, action in rules:
			g.Bind ('r%02d' % i, action)
			i = i + 1

def build_grammar():
	rules = translate_grammar ('form')
	g = kjParseBuild.NullCGrammar()
	g.SetCaseSensitivity(0)
	g.Addterm ("primop", prim_regex, identity)
	g.Addterm ("int", int_regex, int_interp)
	g.Addterm ("var", var_regex, identity)
	g.punct ("(),")
	g.Keywords ('proc if then else := define = begin ; end let in letrecproc')
	g.Nonterms (
		string.join (
			map (lambda x: x[0], grammar), ' '
			)
		)
	g.Declarerules (rules)
	g.Compile()
	g.MarshalDump (open ('grammar.bin', 'wb'))
	return g

def load_grammar():
	import kjParser
	g = kjParser.UnMarshalGram (open ('grammar.bin', 'rb'))
	# declare terminals
	g.Addterm ("primop", prim_regex, identity)
	g.Addterm ("int", int_regex, int_interp)
	g.Addterm ("var", var_regex, identity)
	return g

import os
import stat

def make_grammar():

	if os.path.isfile ('grammar.bin') and os.stat ('parser.py')[stat.ST_MTIME] < os.stat ('grammar.bin')[stat.ST_MTIME]:
		print 'loading parser...'
		g = load_grammar()
	else:
		print 'building parser...'
		g = build_grammar()

	bind_rules (g)
	print 'done.'
	return g
