ES6 (Javascript), 250, 171, 154, 149, 147 bytes
A pure Javascript version.
"Metaprogramming" (like most other answers here), converts input program text into a corresponding Javascript program, by applying a number of direct text substitutions to it (i.e. keeping the program structure as is).
Can probably be golfed further.
UPDATE (v2.1)
- Minus two bytes (removed parenthesis in the ternary expression)
- Golfed 5 more bytes off, by using variable for result extraction, and getting rid of extra "[]"
UPDATE (v2)
Just realised that pending commas in ES arrays are totally valid,
so the whole comma normalization code could be removed.
Also followed an excellent advice by @Titus, on optimizing the alphabet lookup.
UPDATE (v1)
Removed duplicate "replace" alias.
UPDATE (v1)
Use a better alphabet: ()=>1+ []=>0 {}=>2* <>=>2/ (each char can be directly re-used as a value or operator)
Replaced reduce() with replace() (alphabet mapping)
Merged constant inlining, open and closing bracket processing into one step
Golfed (v2.1)
s=>eval("o="+s.replace(/./g,r=>"2+1-3*3/"["()[]{}<>".indexOf(r)]).replace(/\d\D?|\D/g,r=>r[1]?r[0]-2+",":r*1?'([':`].reduce((r,a)=>r${r}a)),`)+"o
Golfed (v1)
(s,A="(2)+[1]-{3}*<3>/")=>eval(s[R="replace"](/./g,r=>A[A.indexOf(r)+1])[R](/\d\D?|\D/g,r=>r[1]?r[0]-2+",":(r[0]*1?'([':`].reduce((r,a)=>r${r}a)),`))[R](/,(\])|,$/g,"$1"))
Golfed (v0)
([...s],A="(a)b[c]d{e}f<g>h",R="replace")=>eval(s.reduce((r,c)=>r+=A[A.indexOf(c)+1],'')[R](/ab|cd|ef|gh/g,r=>({d:-1,b:'0'}[r[1]]||1) + ',')[R](/[aceg]/g,"([")[R](/[bdfh]/g,r=>`].reduce((r,a)=>r${"+*-/"["bfdh".indexOf(r)]}a)),`)[R](/,(\])|,$/g,"$1"))
Explained (v0)
//BEGIN
//s - input text, A - alphabet, R - "String.replace()" alias
E=([...s],A="(a)b[c]d{e}f<g>h",R="replace")=>eval(
//Replace input alphabet by a more friendly one, to avoid too much escaping and quoting
// () - ab, [] -cd, {} - ef, <> - gh
s.reduce((r,c)=>r+=A[A.indexOf(c)+1],'')
//Replace no-arg invocations with a corresponding constant value
// () => 0, [] => -1, {} => 1, <> => 1
[R](/ab|cd|ef|gh/g,r=>({d:-1,b:'0'}[r[1]]||1) + ',')
//Replace opening brackets with "(["
[R](/[aceg]/g,"([")
//Replace closing brackets with "].reduce(...)),"
//An arithmetic operation to apply (+-*/) is chosen based on the bracket type
//and is substituted into the template
[R](/[bdfh]/g,r=>`].reduce((r,a)=>r${"+*-/"["bfdh".indexOf(r)]}a)),`)
//Strip excessive commas
[R](/,(\])|,$/g,"$1")
);
//END: eval() the result
Example:
E("{([]<>()<>{})(<><>)}")
=> eval("([([-1,1,0,1,1].reduce((r,a)=>r+a)),([1,1].reduce((r,a)=>r+a))].reduce((r,a)=>r*a))")
=> 4
Test
E=([...s],A="(a)b[c]d{e}f<g>h",R="replace")=>eval(s.reduce((r,c)=>r+=A[A.indexOf(c)+1],'')[R](/ab|cd|ef|gh/g,r=>({d:-1,b:'0'}[r[1]]||1) + ',')[R](/[aceg]/g,"([")[R](/[bdfh]/g,r=>`].reduce((r,a)=>r${"+*-/"["bfdh".indexOf(r)]}a)),`)[R](/,(\])|,$/g,"$1"))
T=(s,a)=>{
console.log(s,r=E(s),r==a?"OK":"NOT OK");
}
T("()",0)
T("(()())",0)
T("([][])",-2)
T("({}<>)",2)
T("({}[])",0)
T("[]",-1)
T("[[][]]",0)
T("[()<>]",-1)
T("{()}",0)
T("{([]<>)}",0)
Test Output
() 0 OK
(()()) 0 OK
([][]) -2 OK
({}<>) 2 OK
({}[]) 0 OK
[] -1 OK
[[][]] 0 OK
[()<>] -1 OK
{()} 0 OK
{([]<>)} 0 OK
13I have a great name for this, which I totally just made up and did not get from anywhere else: Brain-flake! – ETHproductions – 2016-11-19T02:44:40.717
4@ETHproductions no – Oliver Ni – 2016-11-19T02:45:49.397
Sort of related – James – 2016-11-19T02:57:35.680
2Is division float division or integer division? – xnor – 2016-11-19T03:19:53.067
@xnor Integer division – Oliver Ni – 2016-11-19T04:28:12.340
1@Oliver How should integer division work when one or both of the operands are negative? What are the 4 expected results for
5/3
,5/-3
,-5/3
and-5/-3
? – Martin Ender – 2016-11-19T13:12:02.063@Oliver Must the result be an integer on its own, or can it be wrapped in a list/array/1-tuple? – Copper – 2016-11-19T13:23:22.613
Uhm ... can I use float division please? The type of division doesn´t really matter for the challenge imo. And it doesn´t make a difference for the given test cases. – Titus – 2016-11-19T18:00:13.020
@Oliver @Martin_Ender If you don't want to specify division for negative operands (a bit late now), you might at least demand consistency, such that
<a b c ...>
must be the same as<a {b c ...}>
. (With the definition of<>
, that is kind of obvious.) An interesting case is<{}([][])[]>
vs<{}{([][])[]}>
. – Christian Sievers – 2016-11-23T18:30:20.823