17
\$\begingroup\$

Inspired by, and in memory of, our beloved genius,

John Scholes, 1948-2019

R.I.P.

He invented and implemented dfns — his magnum opus and the subject of the challenge.

For the interested: latest full dfns documentation and videos with John.

Task

Given an ASCII source code, answer in which of the following four categories it belongs:

  1. Dyadic dop

  2. Monadic dop

  3. Dfn

  4. Other

You may return any four consistent values, but please state your mapping if it isn't obvious.

Details

You may assume that the source code always begins with an opening curly brace { and ends with a closing curly brace }.

Recursively nested braces can occur (e.g. {{{}}}), but categories 1–3 can never have brace nesting depth go below 1 (so {}{} is "Other") and all braces must be balanced (so {{} is "Other").

Characters in the following contexts on a line are ignored:

  1. To the right of # (a comment): significant#ignored

  2. Enclosed in single quotes '' (i.e. in a string): significant'ignored'significant (This applies to # too: '#'significant)

  3. To the right of an unpaired quote ' (pairing quotes from the left): significant'ignored

In curly brace level one (i.e. excluding nested braces):

  • Dyadic dops contain the uninterrupted phrase ww

  • Monadic dops do not contain ww, but do contain aa

  • Dfns contain neither ww nor aa

Test cases

Dyadic dops

{ww}
{
    www
}
{
''ww'
}
{aa

ww}
{'#''#'ww?aa}

Monadic dops

{aa}
{aaaa}
{aa{ww}'ww'}
{w#w'
aa'
}
{aaw*w}
{w'\'aa\''}

Dfns

{}
{a a}
{aA}
{
{aa}
}
{w
w''w#
w}
{{
}}
{w\'aa\'}

Other

{}{}
{{}
{}}
{ww}}
{}
{}
{ww}{}
{#}
{'
'}
\$\endgroup\$
  • \$\begingroup\$ @LuisfelipeDejesusMunoz Dfn. If you see a reason to think otherwise, please let me know. \$\endgroup\$ – Adám Feb 19 at 13:38
  • 1
    \$\begingroup\$ Can string quotes be escaped, and if so do we need to handle them? (eg: {'#\'ww?aa'} -> other?) \$\endgroup\$ – Οurous Feb 19 at 21:27
  • 1
    \$\begingroup\$ @Οurous No, the spec is as stated: Anything in quotes is insignificant. (Indeed, APL strings have no escape mechanism.) I'll add a case. \$\endgroup\$ – Adám Feb 20 at 5:14
  • \$\begingroup\$ Hm, may we assume that strings will not contain '' (apostrophe in string, can also be parsed as two adjacent strings for this challenge)? \$\endgroup\$ – Erik the Outgolfer Feb 20 at 16:23
  • \$\begingroup\$ @EriktheOutgolfer It may occur. I'll add a case, but as you say, it doesn't matter whether 'abc''def' is parsed as one or two strings for this challenge. \$\endgroup\$ – Adám Feb 20 at 16:57
9
\$\begingroup\$

JavaScript (ES6),  145 ... 138  136 bytes

Returns \$0\$ for dyadic dops, \$1\$ for monadic dops, \$2\$ for dfns and \$3\$ for other.

s=>[...s].map(c=>s=(n=`
aw}{#'`.indexOf(c))?n>5?i^=2:i?0:n>4?i=1:n>3?d++:n>2?x+=!d--:(o|=n<0|d|s!=c?0:n,c):i=0,o=i=0,x=d=-1)|x|~d?3:2>>o

Try it online!

Alternate versions

  • 131 bytes by taking an array of characters as input
  • 132 bytes by using Buffer() (Node.js)

How?

The input string is parsed character by character.

Translation of characters into codes

Each character \$c\$ is translated into a code \$n\$ according to the following table:

 character | code | triggered operation
-----------+------+---------------------------------------------------------
    \n     |   0  | i = 0
     a     |   1* | o |= n < 0 | d | s != c ? 0 : n
     w     |   2* | o |= n < 0 | d | s != c ? 0 : n
     }     |   3* | x += !d--
     {     |   4* | d++
     #     |   5* | i = 1
     '     |   6  | i ^= 2
   other   |  -1* | same as 'a' or 'w', but always fails because of 'n < 0'

Codes marked with a '\$*\$' have no effect when \$i\neq 0\$.

Variables describing the parser state

The following variables are used during the parsing:

  • \$o\$: a bitmask keeping track of encountered phrases

    • bit 0: a valid phrase aa has been encountered
    • bit 1: a valid phrase ww has been encountered
  • \$i\$: a bitmask telling whether some characters should be ignored

    • bit 0: we're currently located inside a comment
    • bit 1: we're currently located inside a string (this bit is still updated within a comment, but this is harmless)
  • \$s\$: the result of the previous iteration

  • \$d\$: the current depth of nested braces (starting at \$-1\$)
  • \$x\$: the number of times we went from depth \$0\$ to depth \$-1\$, minus \$1\$

Final result

We output \$3\$ if \$x\$ is not equal to \$0\$ or \$d\$ is not equal to \$-1\$. Otherwise, we output \$0\$, \$1\$ or \$2\$ according to the value held in \$o\$.

\$\endgroup\$
6
\$\begingroup\$

Jelly,  50 48 46  45 bytes

Ỵµṣ”'m2Kṣ”#Ḣ)KµċⱮƤØ{IF©<-oµ⁾waż¤ẇ€‘Ḅ«5×®¬Ḅ⁼1¤

A monadic Link accepting a list of characters which yields:

5 - Dyadic dop
4 - Monadic dop
3 - Dfn
0 - Other

Try it online! Or see a test-suite.
uses Python quoting to avoid the possibility of evaluating input as a Python set or dictionary

How?

Ỵµṣ”'m2Kṣ”#Ḣ)KµċⱮƤØ{IF©<-oµ⁾waż¤ẇ€‘Ḅ«5×®¬Ḅ⁼1¤ - Link: list of characters
                                              - breaking long Link up...
Ỵµṣ”'m2Kṣ”#Ḣ)K      - Replace any strings or comments with (single) spaces
Ỵ                   - split at newline characters
 µ          )       - monadic chain for each:
  ṣ”'               -   split at apostrophe characters
     m2             -   modulo 2 slice (i.e. every other part starting with the first
                    -   - that is, non-string code. Will remove strings from within comments)
       K            -   join with spaces
        ṣ”#         -   split at octothorp characters
           Ḣ        -   head (i.e. non-comment code)
             K      - join with spaces

µċⱮƤØ{IF©<-oµ       - Replace characters of code at depth > 1 with the integer 1
µ           µ       - monadic chain, call previous result A:
    Ø{              -   literal ['{','}']
   Ƥ                -   for prefixes of A:
  Ɱ                 -     map across right argument with:
 ċ                  -       count
      I             -   deltas (i.e. [count('}') - count('{')] for each prefix)
       F            -   flatten (i.e. count('}') - count('{') for each prefix)
                    -   -- i.e -1*depth of each character of A; closing braces at depth+1
        ©           -   (copy this list of depths to the register for later use)
         <-         -   less than -1? (vectorises)
           o        -   logical OR with A (vectorises, replacing deep code with 1s)

⁾waż¤ẇ€‘Ḅ«5×®¬Ḅ⁼1¤ - Categorise the result, R
    ¤              - nilad followed by link(s) as a nilad:
⁾wa                -   literal ['w', 'a']
   ż               -   zip with itself = [['w','w'],['a','a']]
     ẇ€            - for €ach: is a sublist of R?  i.e. one of: [0,0] [1,0] [0,1] [1,1]
       ‘           - increment (vectorises)                     [1,1] [2,1] [1,2] [2,2]
        Ḅ          - unbinary                                     3     5     4     6
         «5        - minimum with five                            3     5     4     5
                 ¤ - nilad followed by link(s) as a nilad:
            ®      -   recall depths from the register
             ¬     -   logical NOT (vectorises) (0->1, other depths->0)
              Ḅ    -   unbinary
               ⁼1  -   equal one -- i.e. depths ends with a 0 and contains no other zero 
           ×       - multiply
\$\endgroup\$
3
\$\begingroup\$

Clean, 309 293 284 bytes

We can get away with only using 3 variable names at a time, so we'll call them a, p, and l.

import StdEnv,Text,Data.List
a=isInfixOf o zip2[2,2]
@ =['\'']
$p#p=join@[foldl((\[a:p]_|p>[]=a++[';':join@(tl p)]=split['#']a!!0)o split@)l l\\l<-mklines p]
#l=[(?a- ?l,p)\\a<-inits p&[p:l]<-tails p]
|{p\\(a,p)<-l|a<2}<>"{}"=0|a['ww']l=1|a['aa']l=2=3
?l=sum[1\\'{'<-l]-sum[1\\'}'<-l]

Try it online!

Defines the function $ :: [Char] -> Int and some helpers, giving the mapping:

  • 0: Other
  • 1: Dyadic dop
  • 2: Monadic dop
  • 3: Dfn

Expanded (first version), and with more than 3 variable names:

$ s
    # s // remove strings and comments
        = join [';'] [ // join the second argument with semicolons
            limit ( // take the first repeated element of
                iterate // an infinite list of applications of the first argument
                    (
                        (
                            \(p, q) = p ++ case q of // join the first half with a modified second half
                                ['\'\'': q] = [';': q] // replace two quotes with a semicolon
                                [c, _: q] = [c: q] // drop the character after a quote or hash
                                _ = [] // leave unmatched strings unchanged
                        ) o span // split the string on the first occurrence of
                            \c = c <> '\'' && c <> '#' // a quote or hash
                    ) l // applied to l
                )
            \\ l <- mklines s // for line l in s split at newlines
        ]
    # b // generate a map of nesting levels
        = [
            ( // the pair of
                ?i - ?t, // the nesting level
                c // the character c
            ) 
            \\ i <- inits s // for init i of s
            & // synchronously iterated with 
                [c: t] <- tails s // character c at the start of tail [c:t] of s
        ]
    // determine what the code is
    | {c \\(n, c) <- b | n < 2} // if the string made of characters with nesting level of less than 2
        <> "{}" // is not the same as the paired braces at the beginning and end
        = 0 // return zero
    | m ['ww'] b // if there are two consecutive 'w's at nesting level 2
        = 1 // return 1
    | m ['aa'] b // if there are two consecutive 'a's at nesting level 2
        = 2 // return 2
    = 3 // otherwise return 3
\$\endgroup\$
0
\$\begingroup\$

Retina 0.8.2, 91 bytes

m`'.*?('|$)|#.*
¶
s(+`(?!^)\{[^{}]*\}(?!$)
¶
^(?!\{[^{}]*\}$).+
3
^.+ww.+
2
^.+aa.+
1
..+
0

Try it online! Link includes test suite. Explanation:

m`'.*?('|$)|#.*
¶

Remove strings and comments.

s(+`(?!^)\{[^{}]*\}(?!$)
¶

Remove matched brackets, working out from the innermost, but leave the first and last brackets.

^(?!\{[^{}]*\}$).+
3

If we don't have matched brackets then this is Other.

^.+ww.+
2

Otherwise if we have ww then this is Dyadic Dop.

^.+aa.+
1

Otherwise if we have aa then this is Monadic Dop.

..+
0

Otherwise if this is anything not covered above then this is Dfn.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.