Oh, i think I get it!! I had thought that “list” meant that there was a list within the square brackets, not that there would be multiple instances of [PropValue].
Am I understanding correctly then that ‘elist’ is notated in the same way as ‘list of’, but with the option of a single instance of empty square brackets?
Yes, and that option of a “single” instance of just [] is used to essentially denote an empty list, but still the presence of the property, whereas other “lists” must have one or more property values that are each non-empty strings.
I just realized, is your username supposed to be pronounced “Be Hidden”? I’ve been pronouncing it “b-h-wyden” this whole time
I think EBNF can be pronounced kinda like “evanescence” - but leave off the second ‘s’ sound at the end.
it was because of this additional syllable that i saw the light (i thought this was a pun at first until i looked up the word and found out “evanescence” has nothing to do with light)
I’ve been working on my parser all week!! But last night I had a revelation and had to start from scratch, because I learned about tokenization. I also learned about JSDoc today and started writing annotations for my functions!! Which I love doing, even if I’m getting ahead of myself because most of the functions get rewritten every couple hours while I’m working out bugs idk why but documentation is so much fun to me right now
I would SO be down to do this. I’ve been really focused on my sgf project so I’m learning a lot! Just give me a few years to learn enough to be able to even locate where that is in the OGS GitHub
I have another SGF question that I’m wondering whether somebody here could answer!!
It seems to me like ‘move’ properties with the property identifiers “B” and “W” always get their own node that never is shared with another “B”/“W” property, but I can’t find in the SGF spec where it says that this has to be the case. Can one node have multiple B or W moves? And if so, would this mean that both moves happen simultaneously?
edited to add another question - does the AP root property refer to the application used to generate the original game tree, or should it be used to refer to the last application to make changes to the game tree? And if the former, at what number of edits does it become a new game tree? Is this a philosophical ship of theseus thing or is there a practical answer in this case?
As someone who can and does build compilers and interpreters (as in, it’s literally a part to of my job), my first instinct for parsing for arbitrary data format is “go pull down a bunch of examples and parse them naively”.
There’s absolutely no reason to write a full-fledged SGF parser if all you’re doing is parsing played games, for example.
Im in the process of building a Go library in .Net, and my SGF and GTP parsers are laughably simple, since I only use them in the process of supporting play in my engine.
Baby steps, in all things.
I realize I’m responding to something from over a week ago, I just really want to encourage learning as a marathon, not a sprint.
The SGF spec is kind of annoyingly presented. There are a lot of rules spread across multiple HTML pages that aren’t even that easy to navigate.
This restriction is mentioned on this page SGF properties (FF[4]), under “Function” sections for the B and W properties, it says “B and W properties must not be mixed within a node.”
AB (add black), AW (add white), and AE (add empty) are setup properties that place black, white, or remove stones from the board position. These are different from the move properties (B and W), and must cannot be used in nodes with a move property. Thus, each node in a valid SGF file should belong to exactly one of the following classes:
The node does not have any setup or move properties (i.e., No AB, AW, AE, B, or W)
The node has one move property (i.e., B or W, not both).
The node has one or more setup properties (i.e., any number of AB, AW, AE).
The usage of setup properties should be self-consistent (i.e., no conflicting stone change, e.g., AB[aa] and AW[aa]) and necessary (i.e., don’t set a stone that is already in that state from the parent node).
Setup and move properties behave quite differently. After playing a move with the B or W property, the board position should be updated according to the standard capture rules (including processing suicidal captures, if no normal captures happen). The usage of the move properties allows illegal moves (breaking even the basic ko rule, and even illegal moves that overwrite an existing stone), but usually results in a locally legal board position (since adjacent chains with zero liberties should be resolved by captures), however, the overall board position could still be illegal, if there were other zero-liberty chains not affected the move played.
Setup stones basically edit the board position, which is useful for setting up diagrams, tsumego, or fixed handicap stones. However, after placing stones using setup properties, no captures are resolved, and it is possible to create illegal board positions (with zero-liberty chains) using setup stones.
As for OGS failing the parse the standards-compliant SGF example given by @shinuito, I believe the specific issue is that OGS does not handle the patterns like [ca:cc], which is specifies a rectangle of points given by treating the two coordinates as corners of the rectangle. Thus, [ca:cc] is shorthand for [ca][cb][cc].
I feel like as-worded in the docs, it is philosophical. But if I were implementing an SGF editor myself, I’d update the AP field any time a user saves a file. A practical benefit is that it can allow readers watch out for editor specific anomalies (e.g. saving every node as a variation) or make editor-specific optimizations (e.g. this editor/version never outputs elists, we don’t have to parse them).
My parsing function finally does basically what I want it to do!!
There’s a lot I want to clean up, and I’m sure there are some fundamental things I’ll rewrite as I learn more fundamental things, but I’m happy that it is somewhat functional ^.^
It’s here on GitHub if any of y’all are curious. I’m completely open to (and grateful for) any feedback but certainly am not expecting it from anyone!!! I’m mostly just excited that I made a thing that basically works and can maybe start adding features.
(edit: I renamed it “Kosumi” after my friend’s cat, and also hosted it on GitHub Pages as per @yebellz’s suggestion)
I really appreciate this advice! When I poke my friends with questions about basic JavaScript questions, they’ll sometimes explain a couple ways to optimize it, but they’ll also encourage me to optimize for understanding rather than speed, which is what I’m trying to do
Whenever somebody tells me “best practice” or a more optimal way to do things, I try my best to incorporate it, but I also want to write code I actually can follow. Otherwise it feels like I’m writing somebody else’s code, and not mine
I swear i read these paragraphs multiple times, but missed that one line underneath each of them every time i am not good with blocks of text (she says, responding in multi-paragraph messages)
This is a very clear and practical answer to my question - it’s the opposite of what my best guess was, but it makes perfect sense. Thanks ^.^
It may be more convenient for downstream applications to parse into a tree of board positions (i.e., where each node contains either one move or setup stones or neither), rather than using an object structure that follows the “game trees” and “sequences” structure implied by the grammar. The way the grammar is defined with these concepts is mainly just a concise way to capture how unnecessary parentheses (which are made unnecessary since nodes are prefixed with a semi-colon) can be avoided. This page SGF - Variations gives examples of how to interpret various examples as trees of board positions.
Let me clarify with an example. Here is the SGF of a silly 3-move game (Black opens at the 3-3 point in the top-left, White invades underneath at the 2-2 point, and then Black players under again at 1-1):
(;B[cc];W[bb];B[aa])
The above is roughly how most SGF software would produce output (minus other boilerplate things like FF, AP, game info, etc.). An exception is how OGS would instead generate something like this for the same game:
(;B[cc](;W[bb](;B[aa])))
This essentially represents the same thing, but with the bad style of using a bunch of unnecessary parentheses (when there are no actual variations to represent), and SGF software should generally interpret both examples in the same way.
Consider how your parser generates two different data structures for the above two examples. Also, consider how easy or difficult it may be to traverse and edit those structures.
There are almost always things to improve. If you want, you could:
Rethink the use of the continue statements in matchingSqBracket().
Think about whether parseTokens() could be implemented as a recursive function.
Reword your documentation to let it better reflect how it is commonly done:
/**
* Casts `array` to a slice if it's needed.
*
* @private
* @param {Array} array The array to inspect.
* @param {number} start The start position.
* @param {number} [end=array.length] The end position.
* @returns {Array} Returns the cast slice.
*/