// Goldie: GOLD Engine for D
// Samples: Calculator
// Written in the D programming language.

/** 
A simple arithmetic calculator utilizing a calculator grammar
compiled by GOLD.

$(WEB http://www.dsource.org/projects/goldie, Goldie Homepage)

Author:
$(WEB www.semitwist.com, Nick Sabalausky)

Prerequisites for Building:
- [Required]    DMD 1.043 (LDC untested)
- [Required]    Tango 0.99.8
- [Required]    SemiTwistDTools trunk r82
- [Recommended] Rebuild 0.76
DMD 1.044+ with Tango Trunk might work, but is untested.
Rebuild 0.78 might work, but is quirky and not recommended.
*/

module samples.calculator.main;

import tango.io.Stdout;
import tango.text.Unicode;
import tango.util.Convert;

import semitwist.util.text : sformat;
import semitwist.cmd.plain;

import goldie.all;

real calculate(TokGold tok)
{
	switch(tok.name)
	{
	case "Number":
		return to!(real)(tok.toString());
	
	// <Add Exp> ::=
	case "Add Exp":
	
		// <Mult Exp>
		if(tok.length == 1)
			return calculate(tok[0]);

		// <Add Exp> '+' <Mult Exp>
		if(tok[1].name == "+")
			return calculate(tok[0]) + calculate(tok[2]);

		// <Add Exp> '-' <Mult Exp>
		return calculate(tok[0]) - calculate(tok[2]);
		
	// <Mult Exp> ::=
	case "Mult Exp":

		// <Negate Exp>
		if(tok.length == 1)
			return calculate(tok[0]);

		// <Mult Exp> '*' <Negate Exp>
		if(tok[1].name == "*")
			return calculate(tok[0]) * calculate(tok[2]);
		
		// <Mult Exp> '/' <Negate Exp>
		return calculate(tok[0]) / calculate(tok[2]);
		
	// <Negate Exp> ::=
	case "Negate Exp":

		// <Value> 
		if(tok.length == 1)
			return calculate(tok[0]);

		// '-' <Value>
		return -calculate(tok[1]);
	
	// <Value> ::=
	case "Value":

		// Number
		if(tok.length == 1)
			return calculate(tok[0]);

		// '(' <Add Exp> ')'
		return calculate(tok[1]);
		
	default:
		throw new Exception("Unexpected name: {}".sformat(tok.name));
	}
}

int main(char[][] args)
{
	Stdout("Calculator").newline;
	
	Stdout("Loading language...").flush;
	GoldLanguage lang = Goldie.loadLanguage("calc");
	Stdout("done!").newline;

	Stdout("Enter an arithmetic expression ('exit' to exit)").newline.newline;
	while(true)
	{
		char[] src = cmd.prompt("calculator>");
		
		if(src.toLower() == "exit")
			break;
			
		try
		{
			real result = calculate( lang.parseCode(src).parseTree );
			Stdout.formatln("Result: {:d5}", result);
		}
		catch(Exception e)
			Stdout.formatln("ERROR: {}", e);
	}
	
	return 0;
}
