C# (with much LINQ), 150 lines
Ok, I implement a monadic parser combinator library, and then use that library to solve this problem. All told it's about 150 lines of code. (This is basically a straight transliteration of my F# solution.)
I assume exponentiation associates to the right, and the other operators associate to the left. Everything works on System.Doubles. I did not do any error handling for bad inputs or divide-by-zero.
using System;
using System.Collections.Generic;
using System.Linq;
class Option
{
public T Value { get; set; }
public Option(T x) { Value = x; }
}
delegate Option>> P(List input);
static class Program
{
static List Cons(T x, List xs)
{
var r = new List(xs);
r.Insert(0, x);
return r;
}
static Option Some(T x) { return new Option(x); }
static KeyValuePair> KVP(T x, List y)
{ return new KeyValuePair>(x,y); }
// Core Parser Library
static P Fail() { return i => null; }
static P Select(this P p, Func f)
{
return i =>
{
var r = p(i);
if (r == null) return null;
return Some(KVP(f(r.Value.Key),(r.Value.Value)));
};
}
public static P SelectMany(this P p, Func> sel, Func prj)
{
return i =>
{
var r = p(i);
if (r == null) return null;
var p2 = sel(r.Value.Key);
var r2 = p2(r.Value.Value);
if (r2 == null) return null;
return Some(KVP(prj(r.Value.Key, r2.Value.Key),(r2.Value.Value)));
};
}
static P Or(this P p1, P p2)
{
return i =>
{
var r = p1(i);
if (r == null) return p2(i);
return r;
};
}
static P Sat(Func pred)
{
return i =>
{
if (i.Count == 0 || !pred(i[0])) return null;
var rest = new List(i);
rest.RemoveAt(0);
return Some(KVP(i[0], rest));
};
}
static P Return(T x)
{
return i => Some(KVP(x,i));
}
// Auxiliary Parser Library
static P Digit = Sat(Char.IsDigit);
static P Lit(char c, T r)
{
return from dummy in Sat(x => x == c)
select r;
}
static P> Opt(P> p)
{
return p.Or(Return(new List()));
}
static P> Many(P p)
{
return Many1(p).Or(Return(new List()));
}
static P> Many1(P p)
{
return from x in p
from xs in Many(p)
select Cons(x, xs);
}
static P Chainl1(this P p, P> op)
{
return from x in p
from r in Chainl1Helper(x, p, op)
select r;
}
static P Chainl1Helper(T x, P p, P> op)
{
return (from f in op
from y in p
from r in Chainl1Helper(f(x, y), p, op)
select r)
.Or(Return(x));
}
static P Chainr1(this P p, P> op)
{
return (from x in p
from r in (from f in op
from y in Chainr1(p, op)
select f(x, y))
.Or(Return(x))
select r);
}
static P TryParse(string s)
{
double d;
if (Double.TryParse(s, out d)) return Return(d);
return Fail();
}
static void Main(string[] args)
{
var Num = from sign in Opt(Lit('-', new List(new []{'-'})))
from beforeDec in Many(Digit)
from rest in Opt(from dec in Lit('.','.')
from afterDec in Many(Digit)
select Cons(dec, afterDec))
let s = new string(Enumerable.Concat(sign,
Enumerable.Concat(beforeDec, rest))
.ToArray())
from r in TryParse(s)
select r;
// Expression grammar of this code-golf question
var AddOp = Lit('+', new Func((x,y) => x + y))
.Or(Lit('-', new Func((x, y) => x - y)));
var MulOp = Lit('*', new Func((x, y) => x * y))
.Or(Lit('/', new Func((x, y) => x / y)));
var ExpOp = Lit('^', new Func((x, y) => Math.Pow(x, y)));
P Expr = null;
P Term = null;
P Factor = null;
P Part = null;
P Paren = from _1 in Lit('(', 0)
from e in Expr
from _2 in Lit(')', 0)
select e;
Part = Num.Or(Paren);
Factor = Chainr1(Part, ExpOp);
Term = Chainl1(Factor, MulOp);
Expr = Chainl1(Term, AddOp);
Func CodeGolf = s =>
Expr(new List(s)).Value.Key.ToString();
// Examples
Console.WriteLine(CodeGolf("1.1+2.2+10^2^3")); // 100000003.3
Console.WriteLine(CodeGolf("10+3.14/2")); // 11.57
Console.WriteLine(CodeGolf("(10+3.14)/2")); // 6.57
Console.WriteLine(CodeGolf("-1^(-3*4/-6)")); // 1
Console.WriteLine(CodeGolf("-2^(2^(4-1))")); // 256
Console.WriteLine(CodeGolf("2*6/4^2*4/3")); // 1
}
}