Prelude Syntax-Checker

Python 2, 128 119 105 bytes

def F(p):
 v=n=1
 for r in map(None,*p.split("\n")):A,B=map(r.count,"()");n+=B-A;v*=n<2>A+B
 return n*v>0

Did you know that you can map None in Python 2?

Explanation

We want to start by transposing the Prelude so that columns become rows. Usually we would do this with zip, but since zip truncates to the shortest row length and itertools.zip_longest is far too long for code-golf, it seems like there's no short way to do what we want...

Except for mapping None:

>>> print map(None,*[[1,2,3],[4],[5,6]])
[(1, 4, 5), (2, None, 6), (3, None, None)]

Unfortunately (or rather, fortunately for all non-golf purposes), this only works in Python 2.

As for n and v:

  • n acts like a stack, counting 1 - <number of unmatched '(' remaining>. For every ( we see we subtract 1, and for every ) we see we add 1. Hence if n >= 2 at any point, then we've seen too many )s and the program is invalid. If n doesn't finish on 1, then we have at least one unmatched ( remaining.
  • v checks validity, and starts at 1. If the program is ever invalid (n >= 2 or A+B >= 2), then v becomes 0 to mark invalidity.

Hence if the program is valid, then by the end we must have n = 1, v = 1. If the program is invalid, then by the end we must either have v = 0, or v = 1, n <= 0. Therefore validity can be succinctly expressed as n*v>0.

(Thanks to @feersum for a multitude of good suggestions which took off 14 bytes!)

Previous, more readable submission:

def F(p):
 p=map(None,*p.split("\n"));v=n=0
 for r in p:R=r.count;A=R("(");B=R(")");n+=A-B;v|=A+B>1or n<0
 return n<1>v

CJam, 57 56 bytes

qN/z_{_"()"--W<},,\s:Q,{)Q/({_')/,\'(/,-}:T~\sT0e>+z+}/!

Too long, can be golfed a lot. Explanation to be added once I golf it.

Brief explanation

There are two checks in the code:

  • First filter checks that each column as at most 1 bracket. The final output of the filter is the number of columns with more than 1 brackets.
  • Second we convert the input into column major format and then split it on each index into two parts.
    • In each of these two parts, (Number of "(" - Number of ")") should be complimenting each other. So when you add them up, it should result to 0. Any part that fails this property makes the whole input have non matching brackets.
    • I also have to make sure that "(" are to the left of ")". This means that the value of Number of "(" - Number of ")" cannot be negative for the right side block.

Try it online here


J, 64 bytes

Input is a string with a trailing newline. Output is 0 or 1.

(0=(1<|s)s+[:(|@{:+(0>])s)[:+/\]s=.+/@:)@(1 _1 0{~[:'()'&i.];.2)

Example usage

   ]in=.'#(#)##',LF,'###',LF,'###(#)',LF
#(#)##
###
###(#)

   ((0=(1<|s)s+[:(|@{:+(0>])s)[:+/\]s=.+/@:)@(1 _1 0{~[:'()'&i.];.2)) in
0

The method is the following

  • cut input at newlines and put into a matrix ];.2
  • map ( / ) / anything else into 1 / -1 / 0 1 _1 0{~[:'()'&i.]
  • define an s=.+/@: adverb which added to a verb sums the verbs array output
  • add values in columns ]s

    • check positive () balance in every prefix [:(0>])s)[:+/\]
    • check equal () balance in the whole list (i.e. in the last prefix) |@{:@]
  • add abs(values) in columns and check every element for a max value of 1 (1<|s)s

  • as all the previous checks yielded positive at failure we add them up and compare to 0 to get the validity of the input 0=]