diff options
| author | Bruce Hill <bitbucket@bruce-hill.com> | 2018-09-12 15:31:59 -0700 |
|---|---|---|
| committer | Bruce Hill <bitbucket@bruce-hill.com> | 2018-09-12 15:32:04 -0700 |
| commit | ea310306d73e0bc6542f7133825549ae4471b06a (patch) | |
| tree | fcc2a5a868fe09fb599d8261f66dad96ac69bb0b /nomsu.4.peg | |
| parent | 9e10c8bf006f42e90a011b8f9284e3ffa52a5859 (diff) | |
Initial working version.
Diffstat (limited to 'nomsu.4.peg')
| -rw-r--r-- | nomsu.4.peg | 208 |
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) + ) |
