Golf Me An OOP!
CJam, 59 bytes
q_'.e=\N/{)'?=\S/>_,(%}%/(__,*{(2$z~@f=.*\m*|}/ff{1$e=>:=N}
This finishes instantly for both test cases.
It either prints the second name name of the question or 1
(both truthy), or 0
(falsy).
Try it online in the CJam interpreter.
Idea
Because of the distinction between classes and members, the challenge boils down to creating a preorder for which the input provides a partial definition.
We define that x ≺ y iff x is a y or x has a y.
For the first test case, the input states that B ≺ A, C ≺ B and A ≺ foo. Because of transitivity, we also have B ≺ foo, C ≺ A and A ≺ foo. Also, because of reflexivity, x ≺ x is always true.
For a given input, we thus can extract the partial definition of ≺ from the statements, apply transitivity a sufficient amount of times to complete the definition of ≺ and finally answer the questions.
Code
q_ e# Push all input from STDIN and a copy.
'.e= e# Count the number of dots/statements (C).
\N/ e# Split the original input at linefeeds.
{ e# For each line:
)'?= e# Pop the last character and check if it is a question mark.
e# Pushes 1 for '?', 0 for '.'.
\S/ e# Split the modified line at spaces.
> e# Remove the first chunk ("Does" or "Is") for questions.
_,(% e# Keep the first and last element of the resulting array.
}%/ e# Split the line array into chunks of length C.
(_ e# Extract the first chunk (statements) and push a copy.
e# The original becomes an accumulator for ≺.
_,* e# Repeat the statements C times.
{ e# For each of the repeated statements:
( e# Shift out the first name.
e# ["w" "x"] -> ["x"] "w"
2$z~ e# Copy the accumulator, zip it and dump.
e# [["x" "y"] ["z" "w"]] -> ["x" "z"] ["y" "w"]
@f= e# Rotate the shifted out name on top and check for equality.
e# ["y" "w"] "w" -> [0 1]
.* e# Vectorized string repetition.
e# ["x" "z"] [0 1] -> ["" "z"]
\m* e# Swap the result with the shifted array and apply Cartesian product.
e# ["" "z"] ["x"] -> [["" "x"] ["z" "x"]]
e# This accounts for transitivity; we had ["w" "x"] and ["z" "w"],
e# so now we have ["z" "x"].
| e# Perform set union with the accumulator to add the new pairs.
}/ e#
ff{ e# For each of the questions on the bottom of the stack.
1$e= e# Count the occurrences of the question pair in the accumulator.
> e# Remove 0 or 1 elements from the question pair.
:= e# Check for equality.
e# If the question pair occurs in the accumulator, this pushes the
e# second name of the question pair. Otherwise, it pushes 1 if the
e# names are equal (to account for reflexivity) and 0 otherwise.
N e# Push a linefeed.
} e#
Python 3, 431 331 308 bytes
o={}
f={}
def h(z,f):
if z not in o:f[z]=[z];o[z]=[]
while 1:
g=input().split(' ');r=2;l=g[-1][:-1]
if'.'in g[3]:
if'i'in g[1]:h(g[0],f);h(l,f);f[g[0]]+=f[l]
if'h'in g[1]:o[g[0]]+=l,
else:
if'I'in g[0]:r=any(l in z for z in f[g[1]])
if'D'in g[0]:r=any(l in o[z] for z in f[g[1]])
if r<2:print(r)
This is the full version with comments
objects = {}
synonyms = {}
def createObject(name):
"""
Create a object with `name` if is does not yet exist and start a synonym tree.
"""
if name not in objects:
synonyms[name] = [name]
objects[name] = []
# use this to read from a file
# with open("questions.txt") as file:
# for l in file:
# print(">>> " + l, end='')
# inArg = l.replace("\n","").split(" ")
while True: # to read from a file comment this
inArg = input(">>> ").split(" ") # and this out
out = -1
if '.' in inArg[3]: # statement
last = inArg[3].replace('.','')
if 'i' in inArg[1]: # is a
createObject(inArg[0])
createObject(last)
synonyms[inArg[0]] += synonyms[last]
if 'h' in inArg[1]: # has a
objects[inArg[0]] += [last]
else:# question
last = inArg[-1].replace('?','')
createObject(inArg[1])
if 'I'in inArg[0]: # Is a
out = any([last in syn for syn in synonyms[inArg[1]]])
if 'D'in inArg[0]: # Does have a
out = any(last in objects[syn] for syn in synonyms[inArg[1]])
if out != -1:
print(out)
Output for test case #1:
True
True
False
Case #2:
True
True
False
False
True
I removed the debug commands for clarity in the main program, but if you would like to see them just look in the history
JavaScript, 265 263 bytes
for(o={};i=prompt().split(/\W/);)a=i[0],b=i[1],d=i[2],b=="is"?((o[a]=o[a]||{p:[],k:{}}).p.push(d),o[d]=o[d]||{p:[],k:{}}):b=="has"?o[a].k[d]=1:alert(o[b]&&(a>"E"?b==d|(c=n=>~(p=o[n].p).indexOf(d)|p.some(c))(b):(c=n=>o[n].k.hasOwnProperty(i[4])|o[n].p.some(c))(b)))
Enter a blank string to quit.
Explanation
for(
o={}; // o = all objects
i=prompt().split(/\W/); // i = command as an array of words
)
a=i[0], // a = first word
b=i[1], // b = second word
//c=i[2], // c = third word
d=i[3], // b = fourth word
//e=i[4], // e = fifth word
// Case: <name> is a <name>.
b=="is"?(
(o[a]=o[a]||{p:[],k:{}}) // create the object if it does not exist
.p.push(d), // add the parent to the object's list of parents
o[d]=o[d]||{p:[],k:{}} // create the parent if it does not exist
):
// Case: <name> has a <name>.
b=="has"?
o[a].k[d]=1 // set the specified property
:
alert( // display the responses to the questions
o[b] // false if the queried object does not exist
&&(
// Case: Is <name> a <name>?
a>"E"? // "Is" > "E" and "Does" < "E"
b==d // check if it is itself
|(c=n=>
~(p=o[n].p) // p = direct parents of current object
.indexOf(d) // check direct parents for the object
|p.some(c) // check the grandparents
)(b)
// Case: Does <name> have a <name>?
:
(c=n=>
o[n].k.hasOwnProperty(i[4]) // check if this object has the property
|o[n].p.some(c) // check it's parents for the property also
)(b)
)
)