Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support markers in neurolucida ascii files #1867

Merged
merged 1 commit into from
Mar 24, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 90 additions & 5 deletions arborio/neurolucida.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ bool symbol_matches(const char* match, const asc::token& t) {
return t.kind==tok::symbol && !std::strcmp(match, t.spelling.c_str());
}

// A list of symbols that indicate markers
bool is_marker_symbol(const asc::token& t) {
return symbol_matches("Dot", t)
|| symbol_matches("OpenCircle", t)
|| symbol_matches("Cross", t);
};


// Parse a color expression, which have been observed in the wild in two forms:
// (Color Red) ; labeled
// (Color RGB (152, 251, 152)) ; RGB literal
Expand Down Expand Up @@ -313,6 +321,74 @@ parse_hopefully<arb::mpoint> parse_spine(asc::lexer& L) {

#define PARSE_SPINE(L, X) if (auto rval__ = parse_spine(L)) X=std::move(*rval__); else return FORWARD_PARSE_ERROR(rval__.error());

parse_hopefully<std::string> parse_name(asc::lexer& L) {
EXPECT_TOKEN(L, tok::lparen);
if (!symbol_matches("Name", L.current())) {
return unexpected(PARSE_ERROR("expected Name symbol missing", L.current().loc));
}

// consume Name symbol
auto t = L.next();
if (t.kind != tok::string) {
return unexpected(PARSE_ERROR("expected a string in name description", t.loc));
}
std::string name = t.spelling;

L.next();
EXPECT_TOKEN(L, tok::rparen);

return name;
}

#define PARSE_NAME(L, X) {if (auto rval__ = parse_name(L)) X=*rval__; else return FORWARD_PARSE_ERROR(rval__.error());}

struct marker_set {
asc_color color;
std::string name;
std::vector<arb::mpoint> locations;
};

[[maybe_unused]]
std::ostream& operator<<(std::ostream& o, const marker_set& ms) {
o << "(marker-set \"" << ms.name << "\" " << ms.color;
for (auto& l: ms.locations) o << " " << l;
return o << ")";

}

parse_hopefully<marker_set> parse_markers(asc::lexer& L) {
EXPECT_TOKEN(L, tok::lparen);

marker_set markers;

// parse marker kind keyword
auto t = L.current();
if (!is_marker_symbol(t)) {
return unexpected(PARSE_ERROR("expected a valid marker type", t.loc));
}
L.next();
while (L.current().kind==tok::lparen) {
auto n = L.peek();
if (symbol_matches("Color", n)) {
PARSE_COLOR(L, markers.color);
}
else if (symbol_matches("Name", n)) {
PARSE_NAME(L, markers.name);
}
else {
arb::mpoint loc;
PARSE_POINT(L, loc);
markers.locations.push_back(loc);
}
}

EXPECT_TOKEN(L, tok::rparen);

return markers;
}

#define PARSE_MARKER(L, X) if (auto rval__ = parse_markers(L)) X=std::move(*rval__); else return FORWARD_PARSE_ERROR(rval__.error());

struct branch {
std::vector<arb::mpoint> samples;
std::vector<branch> children;
Expand Down Expand Up @@ -348,10 +424,11 @@ parse_hopefully<branch> parse_branch(asc::lexer& L) {
};

// One of these symbols must always be present at what Arbor calls a terminal.
auto branch_end_symbol = [] (const asc::token& t) {
return symbol_matches("Incomplete", t)
auto is_branch_end_symbol = [] (const asc::token& t) {
return symbol_matches("Normal", t)
|| symbol_matches("High", t)
|| symbol_matches("Low", t)
|| symbol_matches("Normal", t)
|| symbol_matches("Incomplete", t)
|| symbol_matches("Generated", t);
};

Expand All @@ -365,16 +442,24 @@ parse_hopefully<branch> parse_branch(asc::lexer& L) {
PARSE_POINT(L, sample);
B.samples.push_back(sample);
}
// A marker statement is always of the form ( MARKER_TYPE ...)
else if (t.kind==tok::lparen && is_marker_symbol(p)) {
marker_set markers;
PARSE_MARKER(L, markers);
// Parse the markers, but don't record information about them.
// These could be grouped into locset by name and added to the label dictionary.
}
// Spines are marked by a "less than", i.e. "<", symbol.
else if (t.kind==tok::lt) {
arb::mpoint spine;
PARSE_SPINE(L, spine);
// parse the spine, but don't record the location.
}
// Test for a symbol that indicates a terminal.
else if (branch_end_symbol(t)) {
else if (is_branch_end_symbol(t)) {
L.next(); // Consume symbol
if (!branch_end(t)) {
return unexpected(PARSE_ERROR("Incomplete, Normal, Low or Generated not at a branch terminal", t.loc));
return unexpected(PARSE_ERROR("Incomplete, Normal, High, Low or Generated not at a branch terminal", t.loc));
}
finished = true;
}
Expand Down