aboutsummaryrefslogtreecommitdiff
path: root/nomsu.4.peg
diff options
context:
space:
mode:
Diffstat (limited to 'nomsu.4.peg')
-rw-r--r--nomsu.4.peg208
1 files changed, 208 insertions, 0 deletions
diff --git a/nomsu.4.peg b/nomsu.4.peg
new file mode 100644
index 0000000..ed119c5
--- /dev/null
+++ b/nomsu.4.peg
@@ -0,0 +1,208 @@
+-- Nomsu version 4
+file:
+ {:curr_indent: ' '* :}
+ (((action / expression / inline_block / indented_block) eol !.)
+ / file_chunks / empty_block)
+ {:curr_indent: %nil :}
+ !.
+
+shebang: "#!" (!"nomsu" [^%nl])* "nomsu" ws+ "-V" ws* {:version: [0-9.]+ :} [^%nl]*
+
+file_chunks (FileChunks):
+ {:curr_indent: ' '* :}
+ shebang? comment? blank_lines?
+ (top_block (section_division nl_nodent top_block)*)
+ blank_lines?
+ ws* unexpected_chunk?
+ {:curr_indent: %nil :}
+
+top_block (Block):
+ {:curr_indent: ' '* :}
+ comment? blank_lines? statement (nl_nodent statement?)*
+ {:curr_indent: %nil :}
+
+empty_block (Block):
+ {:curr_indent: ' '* :}
+ comment? blank_lines?
+ {:curr_indent: %nil :}
+
+nodent: (unexpected_indent [^%nl]* / =curr_indent)
+indent: =curr_indent (" ")
+blank_lines: %nl ((nodent comment / ws*) %nl)*
+eol: ws* eol_comment? (!. / &%nl)
+
+nl_nodent: blank_lines nodent
+nl_indent: blank_lines {:curr_indent: indent :} (comment nl_nodent)*
+
+comment (Comment):
+ "#" {~ [^%nl]* (%nl+ (indent -> '') [^%nl]*)* ~}
+eol_comment (Comment):
+ "#" {[^%nl]*}
+
+unexpected_code: ws* _unexpected_code
+_unexpected_code (Error):
+ {:error: {~ [^%nl]+ -> "Couldn't parse this code" ~} :}
+unexpected_chunk (Error):
+ {:error: {~ .+ -> "Couldn't parse this code" ~} :}
+unexpected_indent (Error):
+ {:error: {~ (=curr_indent ws+) -> "Messed up indentation" ~} :}
+ {:hint: {~ '' -> 'Either make sure this line is aligned with the one above it, or make sure the previous line ends with something that uses indentation, like ":" or "(..)"' ~} :}
+missing_paren_err (Error):
+ {:error: {~ eol -> 'Line ended without finding a closing )-parenthesis' ~} :}
+ {:hint: {~ '' -> 'Put a ")" here' ~} :}
+missing_quote_err (Error):
+ {:error: {~ eol -> 'Line ended before finding a closing double quotation mark' ~} :}
+ {:hint: {~ "" -> "Put a quotation mark here" ~} :}
+missing_bracket_error (Error):
+ {:error: {~ eol -> "Line ended before finding a closing ]-bracket" ~} :}
+ {:hint: {~ '' -> 'Put a "]" here' ~} :}
+missing_brace_error (Error):
+ {:error: {~ eol -> "Line ended before finding a closing }-brace" ~} :}
+ {:hint: {~ '' -> 'Put a "}" here' ~} :}
+
+section_division: ("~")^+3 eol
+
+inline_block:
+ "(" ws* inline_block ws* ")" / raw_inline_block
+raw_inline_block (Block):
+ (!"::") ":" ws* ((inline_statement (ws* ";" ws* inline_statement)*) / !(eol nl_indent))
+indented_block (Block):
+ ":" eol nl_indent statement (nl_nodent statement?)*
+ (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
+ {:curr_indent: %nil :}
+
+statement:
+ (action / expression) (eol / unexpected_code)
+
+inline_statement: (inline_action / inline_expression)
+
+noindex_inline_expression:
+ number / variable / inline_text / inline_list / inline_dict / inline_nomsu
+ / ( "("
+ ws* (inline_action / inline_expression) ws*
+ (ws* ',' ws* (inline_action / inline_expression) ws*)*
+ (")" / missing_paren_err / unexpected_code)
+ )
+inline_expression: index_chain / noindex_inline_expression
+indented_expression:
+ cool_indented_text / indented_text / indented_nomsu / indented_list / indented_dict / ({|
+ "(..)" nl_indent
+ (action / expression) (eol / unexpected_code)
+ (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
+ {:curr_indent: %nil :}
+ |} -> unpack)
+expression:
+ inline_expression / indented_expression
+
+inline_nomsu (EscapedNomsu): "\" (inline_expression / inline_block)
+indented_nomsu (EscapedNomsu):
+ "\" (noindex_inline_expression / inline_block / indented_expression / indented_block)
+
+index_chain (IndexChain):
+ noindex_inline_expression ("." (text_word / noindex_inline_expression))+
+
+-- Actions need either at least 1 word, or at least 2 tokens
+inline_action (Action):
+ !section_division
+ ({:target: inline_arg :} ws* "::" ws*)?
+ ( (inline_arg (ws* (inline_arg / word))+)
+ / (word (ws* (inline_arg / word))*))
+ (ws* inline_block)?
+inline_arg: inline_expression / inline_block
+action (Action):
+ !section_division
+ ({:target: arg :} (nl_nodent "..")? ws* "::" (nl_nodent "..")? ws*)?
+ ( (arg ((nl_nodent "..")? ws* (arg / word))+)
+ / (word ((nl_nodent "..")? ws* (arg / word))*))
+arg: expression / inline_block / indented_block
+
+word: !number { operator_char+ / ident_char+ }
+
+text_word (Text): word
+
+inline_text (Text):
+ !(cool_indented_text / indented_text)
+ ('"' _inline_text* ('"' / missing_quote_err / unexpected_code))
+_inline_text:
+ {~ (('\"' -> '"') / ('\\' -> '\') / escaped_char / [^%nl\"])+ ~}
+ / inline_text_interpolation
+inline_text_interpolation:
+ "\" (
+ variable / inline_list / inline_dict / inline_text
+ / ("("
+ ws* (inline_action / inline_expression) ws*
+ (ws* ',' ws* (inline_action / inline_expression) ws*)*
+ (")" / missing_paren_err / unexpected_code))
+ )
+
+indented_text (Text):
+ '".."' eol %nl {%nl+}? {:curr_indent: indent :}
+ (indented_plain_text / text_interpolation / {~ %nl+ (=curr_indent -> "") ~})*
+ unexpected_code?
+ {:curr_indent: %nil :}
+cool_indented_text (Text):
+ ({|
+ '"' _inline_text* '\' %nl {:curr_indent: indent :} '..'
+ (indented_plain_text / text_interpolation / {~ %nl+ (=curr_indent -> "") ~})*
+ unexpected_code?
+ |} -> unpack)
+ ({(%nl &%nl)+}? %nl =curr_indent '..' _inline_text* '"')?
+-- Tracking text-lines-within-indented-text as separate objects allows for better debugging line info
+indented_plain_text (Text):
+ {~ (("\\" -> "\") / (("\" blank_lines =curr_indent "..") -> "") / (!text_interpolation "\") / [^%nl\]+)+
+ (%nl+ (=curr_indent -> ""))* ~}
+text_interpolation:
+ inline_text_interpolation / ("\" indented_expression (blank_lines =curr_indent "..")?)
+
+number (Number): (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / "0x" [0-9a-fA-F]+ / ([0-9]+)))-> tonumber)
+
+-- Variables can be nameless (i.e. just %) and can only contain identifier chars.
+-- This ensures you don't get weird parsings of `%x+%y` or `%'s thing`.
+variable (Var): "%" {ident_char*}
+
+inline_list (List):
+ !('[..]')
+ "[" ws*
+ (inline_list_item (ws* ',' ws* inline_list_item)* (ws* ',')?)? ws*
+ ("]" / (","? (missing_bracket_error / unexpected_code)))
+indented_list (List):
+ "[..]" eol nl_indent
+ list_line (nl_nodent list_line?)*
+ (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
+ (","? unexpected_code)?
+list_line:
+ (inline_list_item ws* "," ws*)+ eol
+ / (inline_list_item ws* "," ws*)* (action / expression) eol
+inline_list_item: inline_action / inline_expression
+
+inline_dict (Dict):
+ !('{..}')
+ "{" ws*
+ (inline_dict_entry (ws* ',' ws* inline_dict_entry)*)? ws*
+ ("}" / (","? (missing_brace_error / unexpected_code)))
+indented_dict (Dict):
+ "{..}" eol nl_indent
+ dict_line (nl_nodent dict_line?)*
+ (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
+ (","? unexpected_code)?
+dict_line:
+ (inline_dict_entry ws* "," ws*)+ eol
+ / (inline_dict_entry ws* "," ws*)* dict_entry eol
+dict_entry(DictEntry):
+ dict_key (ws* ":" ws* (action / expression))?
+inline_dict_entry(DictEntry):
+ dict_key (ws* ":" ws* (inline_action / inline_expression)?)?
+dict_key:
+ text_word / inline_expression
+
+operator_char: ['`~!@$^&*+=|<>?/-]
+ident_char: [a-zA-Z0-9_] / %utf8_char
+ws: [ %tab]
+
+escaped_char:
+ ("\"->'') (
+ (([xX]->'') ((({[0-9a-fA-F]^2} %number_16) -> tonumber) -> tochar))
+ / ((([0-9] [0-9]^-2) -> tonumber) -> tochar)
+ / ("a"->ascii_7) / ("b"->ascii_8) / ("t"->ascii_9) / ("n"->ascii_10)
+ / ("v"->ascii_11) / ("f"->ascii_12) / ("r"->ascii_13)
+ )