Fixes and improvements, and ported nomsu.3.peg to new tidier syntax.

This commit is contained in:
Bruce Hill 2018-09-14 14:00:48 -07:00
parent 4d48bf359d
commit 7c1f2dfd69
8 changed files with 167 additions and 318 deletions

View File

@ -3,69 +3,94 @@ file:
{:curr_indent: ' '* :} {:curr_indent: ' '* :}
(((action / expression / inline_block / indented_block) eol !.) (((action / expression / inline_block / indented_block) eol !.)
/ file_chunks / empty_block) / file_chunks / empty_block)
%ws* (!! .+ -> "Parse error" !!)? {:curr_indent: %nil :}
!.
shebang: "#!" (!"nomsu" [^%nl])* "nomsu" %ws+ "-V" %ws* {:version: [0-9.]+ :} [^%nl]* shebang: "#!" (!"nomsu" [^%nl])* "nomsu" ws+ "-V" ws* {:version: [0-9.]+ :} [^%nl]*
file_chunks (FileChunks): file_chunks (FileChunks):
{:curr_indent: ' '* :} {:curr_indent: ' '* :}
shebang? comment? blank_lines? shebang? comment? blank_lines?
(top_block (nl_nodent section_division top_block)*) (top_block (nl_nodent section_division top_block)*)
blank_lines? blank_lines?
ws* unexpected_chunk?
{:curr_indent: %nil :}
top_block (Block): top_block (Block):
{:curr_indent: ' '* :} {:curr_indent: ' '* :}
comment? blank_lines? statement (nl_nodent statement)* comment? blank_lines? statement (nl_nodent statement)*
{:curr_indent: %nil :}
empty_block (Block): empty_block (Block):
{:curr_indent: ' '* :} {:curr_indent: ' '* :}
comment? blank_lines? comment? blank_lines?
{:curr_indent: %nil :}
nodent: =curr_indent !(" ") nodent: (unexpected_indent [^%nl]* / =curr_indent)
indent: =curr_indent " " indent: =curr_indent " "
blank_lines: %nl ((nodent comment / %ws*) %nl)* blank_lines: %nl ((nodent comment / ws*) %nl)*
eol: %ws* eol_comment? (!. / &%nl) eol: ws* eol_comment? (!. / &%nl)
nl_nodent: blank_lines nodent nl_nodent: blank_lines nodent
nl_indent: blank_lines {:curr_indent: indent :} (comment nl_nodent)? nl_indent: blank_lines {:curr_indent: indent :} (comment nl_nodent)*
comment: comment (Comment):
"#" (({} {~ [^%nl]* (%nl+ (indent -> '') [^%nl]*)* ~} %userdata) => add_comment) "#" {~ [^%nl]* (%nl+ (indent -> '') [^%nl]*)* ~}
eol_comment: eol_comment (Comment):
"#" (({} {[^%nl]*} %userdata) => add_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 section_division: ("~")^+3 eol
inline_block: inline_block:
"(" %ws* inline_block %ws* ")" / raw_inline_block "(" ws* inline_block ws* ")" / raw_inline_block
raw_inline_block (Block): raw_inline_block (Block):
(!"::") ":" %ws* ((inline_statement (%ws* ";" %ws* inline_statement)*) / !(eol nl_indent)) (!"::") ":" ws* ((inline_statement (ws* ";" ws* inline_statement)*) / !(eol nl_indent))
indented_block (Block): indented_block (Block):
":" eol nl_indent statement (nl_nodent statement)* (%nl (%ws* %nl)* nodent comment)* ":" eol nl_indent statement (nl_nodent statement)*
(%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
{:curr_indent: %nil :}
statement: statement:
(action / expression) (eol / (!! [^%nl]+ -> "Unexpected code while parsing line" !!)) (action / expression) (eol / unexpected_code)
inline_statement: (inline_action / inline_expression) inline_statement: (inline_action / inline_expression)
noindex_inline_expression: noindex_inline_expression:
number / variable / inline_text / inline_list / inline_dict / inline_nomsu number / variable / inline_text / inline_list / inline_dict / inline_nomsu
/ ( "(" / ( "("
%ws* (inline_action / inline_expression) %ws* ws* (inline_action / inline_expression) ws*
(%ws* ',' %ws* (inline_action / inline_expression) %ws*)* (ws* ',' ws* (inline_action / inline_expression) ws*)*
(")" (")" / missing_paren_err / unexpected_code)
/ (!! eol -> 'Line ended without finding a closing )-parenthesis' !!)
/ (!! [^%nl]+ -> 'Unexpected code while parsing subexpression' !!)
)
) )
inline_expression: index_chain / noindex_inline_expression inline_expression: index_chain / noindex_inline_expression
indented_expression: indented_expression:
indented_text / indented_nomsu / indented_list / indented_dict / ({| indented_text / indented_nomsu / indented_list / indented_dict / ({|
"(..)" nl_indent "(..)" nl_indent
(action / expression) (nl_nodent comment)* (action / expression) (eol / unexpected_code)
(eol / (!! [^%nl]+ -> "Unexpected code while parsing indented expression" !!)) (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
{:curr_indent: %nil :}
|} -> unpack) |} -> unpack)
/ (nl_indent (!! [^%nl]* -> "Unexpected indentation. Perhaps you meant to put a ':' or '(..)' on the previous line?" !!) (nl_nodent [^%nl]*)*)
expression: expression:
inline_expression / indented_expression inline_expression / indented_expression
@ -79,45 +104,42 @@ index_chain (IndexChain):
-- Actions need either at least 1 word, or at least 2 tokens -- Actions need either at least 1 word, or at least 2 tokens
inline_action (Action): inline_action (Action):
!section_division !section_division
({:target: inline_arg :} %ws* "::" %ws*)? ({:target: inline_arg :} ws* "::" ws*)?
( (inline_arg (%ws* (inline_arg / word))+) ( (inline_arg (ws* (inline_arg / word))+)
/ (word (%ws* (inline_arg / word))*)) / (word (ws* (inline_arg / word))*))
(%ws* inline_block)? (ws* inline_block)?
inline_arg: inline_expression / inline_block inline_arg: inline_expression / inline_block
action (Action): action (Action):
!section_division !section_division
({:target: arg :} (nl_nodent "..")? %ws* "::" (nl_nodent "..")? %ws*)? ({:target: arg :} (nl_nodent "..")? ws* "::" (nl_nodent "..")? ws*)?
( (arg ((nl_nodent "..")? %ws* (arg / word))+) ( (arg ((nl_nodent "..")? ws* (arg / word))+)
/ (word ((nl_nodent "..")? %ws* (arg / word))*)) / (word ((nl_nodent "..")? ws* (arg / word))*))
arg: expression / inline_block / indented_block arg: expression / inline_block / indented_block
word: !number { %operator_char+ / %ident_char+ } word: !number { operator_char+ / ident_char+ }
text_word (Text): word text_word (Text): word
inline_text (Text): inline_text (Text):
!('".."' eol) !(indented_text)
'"' ('"' _inline_text* ('"' / missing_quote_err / unexpected_code))
({~ (('\"' -> '"') / ('\\' -> '\') / %escaped_char / [^%nl\"])+ ~} _inline_text:
/ inline_text_interpolation)* {~ (('\"' -> '"') / ('\\' -> '\') / escaped_char / [^%nl\"]+)+ ~}
('"' / inline_text_interpolation
/ (!! eol -> 'Line ended before finding a closing double quotation mark' !!)
/ (!! [^%nl]+ -> 'Unexpected code while parsing Text' !!))
inline_text_interpolation: inline_text_interpolation:
"\" ( "\" (
variable / inline_list / inline_dict / inline_text variable / inline_list / inline_dict / inline_text
/ ("(" / ("("
%ws* (inline_action / inline_expression) %ws* ws* (inline_action / inline_expression) ws*
(%ws* ',' %ws* (inline_action / inline_expression) %ws*)* (ws* ',' ws* (inline_action / inline_expression) ws*)*
(")" (")" / missing_paren_err / unexpected_code))
/ (!! eol -> 'Line ended without finding a closing )-parenthesis' !!)
/ (!! [^%nl]+ -> 'Unexpected code while parsing Text interpolation' !!)))
) )
indented_text (Text): indented_text (Text):
'".."' eol %nl {%nl*} {:curr_indent: indent :} '".."' eol %nl {%nl+}? {:curr_indent: indent :}
(indented_plain_text / text_interpolation / {~ %nl+ (=curr_indent -> "") ~})* (indented_plain_text / text_interpolation / {~ %nl+ (=curr_indent -> "") ~})*
(!! [^%nl]+ -> "Unexpected code while parsing Text" !!)? unexpected_code?
{:curr_indent: %nil :}
-- Tracking text-lines-within-indented-text as separate objects allows for better debugging line info -- Tracking text-lines-within-indented-text as separate objects allows for better debugging line info
indented_plain_text (Text): indented_plain_text (Text):
{~ (("\\" -> "\") / (("\" blank_lines =curr_indent "..") -> "") / (!text_interpolation "\") / [^%nl\]+)+ {~ (("\\" -> "\") / (("\" blank_lines =curr_indent "..") -> "") / (!text_interpolation "\") / [^%nl\]+)+
@ -129,43 +151,51 @@ number (Number): (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / "0x" [0-9a-fA-F]+
-- Variables can be nameless (i.e. just %) and can only contain identifier chars. -- 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`. -- This ensures you don't get weird parsings of `%x+%y` or `%'s thing`.
variable (Var): "%" {%ident_char*} variable (Var): "%" {ident_char*}
inline_list (List): inline_list (List):
!('[..]') !('[..]')
"[" %ws* "[" ws*
(inline_list_item (%ws* ',' %ws* inline_list_item)* (%ws* ',')?)? %ws* (inline_list_item (ws* ',' ws* inline_list_item)* (ws* ',')?)? ws*
("]" / (","? ( ("]" / (","? (missing_bracket_error / unexpected_code)))
(!! eol -> "Line ended before finding a closing ]-bracket" !!)
/(!! [^%nl]+ -> "Unexpected code while parsing List" !!)
)))
indented_list (List): indented_list (List):
"[..]" eol nl_indent "[..]" eol nl_indent
list_line (nl_nodent list_line)* (nl_nodent comment)* list_line (nl_nodent list_line)*
(","? (!! [^%nl]+ -> "Unexpected code while parsing List" !!))? (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
(","? unexpected_code)?
list_line: list_line:
(inline_list_item %ws* "," %ws*)+ eol (inline_list_item ws* "," ws*)+ eol
/ (inline_list_item %ws* "," %ws*)* (action / expression) eol / (inline_list_item ws* "," ws*)* (action / expression) eol
inline_list_item: inline_action / inline_expression inline_list_item: inline_action / inline_expression
inline_dict (Dict): inline_dict (Dict):
!('{..}') !('{..}')
"{" %ws* "{" ws*
(inline_dict_entry (%ws* ',' %ws* inline_dict_entry)*)? %ws* (inline_dict_entry (ws* ',' ws* inline_dict_entry)*)? ws*
("}" / (","? ( ("}" / (","? (missing_brace_error / unexpected_code)))
(!! eol -> "Line ended before finding a closing }-brace" !!)
/ (!! [^%nl]* -> "Unexpected code while parsing Dictionary" !!)
)))
indented_dict (Dict): indented_dict (Dict):
"{..}" eol nl_indent "{..}" eol nl_indent
dict_line (nl_nodent dict_line)* (nl_nodent comment)* dict_line (nl_nodent dict_line)*
(","? (!! [^%nl]+ -> "Unexpected code while parsing Dictionary" !!))? (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
(","? unexpected_code)?
dict_line: dict_line:
(inline_dict_entry %ws* "," %ws*)+ eol (inline_dict_entry ws* "," ws*)+ eol
/ (inline_dict_entry %ws* "," %ws*)* dict_entry eol / (inline_dict_entry ws* "," ws*)* dict_entry eol
dict_entry(DictEntry): dict_entry(DictEntry):
dict_key (%ws* ":" %ws* (action / expression))? dict_key (ws* ":" ws* (action / expression))?
inline_dict_entry(DictEntry): inline_dict_entry(DictEntry):
dict_key (%ws* ":" %ws* (inline_action / inline_expression)?)? dict_key (ws* ":" ws* (inline_action / inline_expression)?)?
dict_key: dict_key:
text_word / inline_expression 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)
)

View File

@ -11,14 +11,14 @@ shebang: "#!" (!"nomsu" [^%nl])* "nomsu" ws+ "-V" ws* {:version: [0-9.]+ :} [^%n
file_chunks (FileChunks): file_chunks (FileChunks):
{:curr_indent: ' '* :} {:curr_indent: ' '* :}
shebang? comment? blank_lines? shebang? comment? blank_lines?
(top_block (section_division nl_nodent top_block)*) (top_block (nl_nodent section_division top_block)*)
blank_lines? blank_lines?
ws* unexpected_chunk? ws* unexpected_chunk?
{:curr_indent: %nil :} {:curr_indent: %nil :}
top_block (Block): top_block (Block):
{:curr_indent: ' '* :} {:curr_indent: ' '* :}
comment? blank_lines? statement (nl_nodent statement?)* comment? blank_lines? statement (nl_nodent statement)*
{:curr_indent: %nil :} {:curr_indent: %nil :}
empty_block (Block): empty_block (Block):
@ -71,7 +71,7 @@ inline_block:
raw_inline_block (Block): raw_inline_block (Block):
(!"::") ":" ws* ((inline_statement (ws* ";" ws* inline_statement)*) / !(eol nl_indent)) (!"::") ":" ws* ((inline_statement (ws* ";" ws* inline_statement)*) / !(eol nl_indent))
indented_block (Block): indented_block (Block):
":" eol nl_indent statement (nl_nodent statement?)* ":" eol nl_indent statement (nl_nodent statement)*
(%nl (ws* %nl)* nodent (comment / eol / unexpected_code))* (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
{:curr_indent: %nil :} {:curr_indent: %nil :}
@ -149,19 +149,16 @@ indented_text (Text):
cool_quote: cool_quote:
'"' !(%nl+ !(=curr_indent)) '"' !(%nl+ !(=curr_indent))
cool_indented_text (Text): cool_indented_text (Text):
({| '"'
'"' _inline_text*
_inline_text* (('\' %nl+ {:curr_indent: indent :} '..')
(('\' %nl+ {:curr_indent: indent :} '..') / disallowed_interpolation? {%nl+} {:curr_indent: indent :})
/ disallowed_interpolation? {%nl+} {:curr_indent: indent :}) (indented_cool_plain_text / text_interpolation / {~ %nl+ (=curr_indent -> "") ~})*
(indented_cool_plain_text / text_interpolation / {~ %nl+ (=curr_indent -> "") ~})* ('"' eol / missing_quote_err)
('"' eol / missing_quote_err)
|} -> unpack)
indented_cool_plain_text (Text): indented_cool_plain_text (Text):
{~ ((("\" blank_lines =curr_indent "..") -> "") / (!text_interpolation ((!("\n") escaped_char) / ('\\' -> '\') / '\')) / (cool_quote / [^%nl"\])+)+ {~ ((("\" blank_lines =curr_indent "..") -> "") / (!text_interpolation ((!("\n") escaped_char) / ('\\' -> '\') / '\')) / (cool_quote / [^%nl"\])+)+
(%nl+ (=curr_indent -> ""))* ~} (%nl+ (=curr_indent -> ""))* ~}
-- Tracking text-lines-within-indented-text as separate objects allows for better debugging line info -- Tracking text-lines-within-indented-text as separate objects allows for better debugging line info
indented_plain_text (Text): indented_plain_text (Text):
{~ (("\\" -> "\") / (("\" blank_lines =curr_indent "..") -> "") / (!text_interpolation "\") / [^%nl\]+)+ {~ (("\\" -> "\") / (("\" blank_lines =curr_indent "..") -> "") / (!text_interpolation "\") / [^%nl\]+)+
@ -182,7 +179,7 @@ inline_list (List):
("]" / (","? (missing_bracket_error / unexpected_code))) ("]" / (","? (missing_bracket_error / unexpected_code)))
indented_list (List): indented_list (List):
"[..]" eol nl_indent "[..]" eol nl_indent
list_line (nl_nodent list_line?)* list_line (nl_nodent list_line)*
(%nl (ws* %nl)* nodent (comment / eol / unexpected_code))* (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
(","? unexpected_code)? (","? unexpected_code)?
list_line: list_line:
@ -197,7 +194,7 @@ inline_dict (Dict):
("}" / (","? (missing_brace_error / unexpected_code))) ("}" / (","? (missing_brace_error / unexpected_code)))
indented_dict (Dict): indented_dict (Dict):
"{..}" eol nl_indent "{..}" eol nl_indent
dict_line (nl_nodent dict_line?)* dict_line (nl_nodent dict_line)*
(%nl (ws* %nl)* nodent (comment / eol / unexpected_code))* (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
(","? unexpected_code)? (","? unexpected_code)?
dict_line: dict_line:

View File

@ -62,12 +62,45 @@ table.copy = function(t)
return _tbl_0 return _tbl_0
end)(), getmetatable(t)) end)(), getmetatable(t))
end end
local make_tree
make_tree = function(tree, userdata)
local cls = AST[tree.type]
tree.source = Source(userdata.filename, tree.start, tree.stop)
tree.start, tree.stop = nil, nil
tree.type = nil
do
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #tree do
local t = tree[_index_0]
if AST.is_syntax_tree(t, "Comment") then
_accum_0[_len_0] = t
_len_0 = _len_0 + 1
end
end
tree.comments = _accum_0
end
if #tree.comments == 0 then
tree.comments = nil
end
for i = #tree, 1, -1 do
if AST.is_syntax_tree(tree[i], "Comment") then
table.remove(tree, i)
end
end
tree = setmetatable(tree, cls)
cls.source_code_for_tree[tree] = userdata.source
if tree.__init then
tree:__init()
end
return tree
end
local Parsers = { } local Parsers = { }
local max_parser_version = 0 local max_parser_version = 0
for version = 1, 999 do for version = 1, 999 do
local _continue_0 = false local _continue_0 = false
repeat repeat
if not (version == 4) then if not (version == 4 or version == 3) then
_continue_0 = true _continue_0 = true
break break
end end
@ -84,40 +117,8 @@ for version = 1, 999 do
break break
end end
max_parser_version = version max_parser_version = version
local make_tree local peg_contents = peg_file:read("*a")
make_tree = function(tree, userdata) Parsers[version] = make_parser(peg_contents, make_tree)
local cls = AST[tree.type]
tree.source = Source(userdata.filename, tree.start, tree.stop)
tree.start, tree.stop = nil, nil
tree.type = nil
do
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #tree do
local t = tree[_index_0]
if AST.is_syntax_tree(t, "Comment") then
_accum_0[_len_0] = t
_len_0 = _len_0 + 1
end
end
tree.comments = _accum_0
end
if #tree.comments == 0 then
tree.comments = nil
end
for i = #tree, 1, -1 do
if AST.is_syntax_tree(tree[i], "Comment") then
table.remove(tree, i)
end
end
tree = setmetatable(tree, cls)
cls.source_code_for_tree[tree] = userdata.source
if tree.__init then
tree:__init()
end
return tree
end
Parsers[version] = make_parser(peg_file:read("*a"), make_tree)
peg_file:close() peg_file:close()
_continue_0 = true _continue_0 = true
until true until true

View File

@ -38,10 +38,25 @@ table.copy = (t)-> setmetatable({k,v for k,v in pairs(t)}, getmetatable(t))
-- consider non-linear codegen, rather than doing thunks for things like comprehensions -- consider non-linear codegen, rather than doing thunks for things like comprehensions
-- Re-implement nomsu-to-lua comment translation? -- Re-implement nomsu-to-lua comment translation?
make_tree = (tree, userdata)->
cls = AST[tree.type]
tree.source = Source(userdata.filename, tree.start, tree.stop)
tree.start, tree.stop = nil, nil
tree.type = nil
tree.comments = [t for t in *tree when AST.is_syntax_tree(t, "Comment")]
if #tree.comments == 0 then tree.comments = nil
for i=#tree,1,-1
if AST.is_syntax_tree(tree[i], "Comment")
table.remove(tree, i)
tree = setmetatable(tree, cls)
cls.source_code_for_tree[tree] = userdata.source
if tree.__init then tree\__init!
return tree
Parsers = {} Parsers = {}
max_parser_version = 0 max_parser_version = 0
for version=1,999 for version=1,999
continue unless version == 4 -- TODO: remove continue unless version == 4 or version == 3 -- TODO: remove
peg_file = io.open("nomsu.#{version}.peg") peg_file = io.open("nomsu.#{version}.peg")
if not peg_file and package.nomsupath if not peg_file and package.nomsupath
for path in package.nomsupath\gmatch("[^;]+") for path in package.nomsupath\gmatch("[^;]+")
@ -49,21 +64,8 @@ for version=1,999
break if peg_file break if peg_file
break unless peg_file break unless peg_file
max_parser_version = version max_parser_version = version
make_tree = (tree, userdata)-> peg_contents = peg_file\read("*a")
cls = AST[tree.type] Parsers[version] = make_parser(peg_contents, make_tree)
tree.source = Source(userdata.filename, tree.start, tree.stop)
tree.start, tree.stop = nil, nil
tree.type = nil
tree.comments = [t for t in *tree when AST.is_syntax_tree(t, "Comment")]
if #tree.comments == 0 then tree.comments = nil
for i=#tree,1,-1
if AST.is_syntax_tree(tree[i], "Comment")
table.remove(tree, i)
tree = setmetatable(tree, cls)
cls.source_code_for_tree[tree] = userdata.source
if tree.__init then tree\__init!
return tree
Parsers[version] = make_parser(peg_file\read("*a"), make_tree)
peg_file\close! peg_file\close!
MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value

View File

@ -113,126 +113,9 @@ setmetatable(NOMSU_DEFS, {
end end
}) })
local Parser = { local Parser = {
version = 3, version = 4,
patterns = { } patterns = { }
} }
do
local peg_tidier = re.compile([[ file <- %nl* {~ (def/comment) (%nl+ (def/comment))* %nl* ~}
def <- anon_def / captured_def
anon_def <- ({ident} (" "*) ":"
{~ ((%nl " "+ def_line?)+) / def_line ~}) -> "%1 <- %2"
captured_def <- ({ident} (" "*) "(" {ident} ")" (" "*) ":"
{~ ((%nl " "+ def_line?)+) / def_line ~}) -> "%1 <- (({} {| %3 |} {} %%userdata) -> %2)"
def_line <- (err / [^%nl])+
err <- ("(!!" { (!("!!)") .)* } "!!)") -> "(({} (%1) %%userdata) => error)"
ident <- [a-zA-Z_][a-zA-Z0-9_]*
comment <- "--" [^%nl]*
]])
for version = 1, Parser.version do
local peg_file = io.open("nomsu." .. tostring(version) .. ".peg")
if not peg_file and package.nomsupath then
for path in package.nomsupath:gmatch("[^;]+") do
peg_file = io.open(path .. "/nomsu." .. tostring(version) .. ".peg")
if peg_file then
break
end
end
end
assert(peg_file, "could not find nomsu .peg file")
local nomsu_peg = peg_tidier:match(peg_file:read('*a'))
peg_file:close()
Parser.patterns[version] = re.compile(nomsu_peg, NOMSU_DEFS)
end
end
local _anon_chunk = 0
Parser.parse = function(nomsu_code, source, version)
if source == nil then
source = nil
end
if version == nil then
version = nil
end
source = source or nomsu_code.source
nomsu_code = tostring(nomsu_code)
if not (source) then
source = Source("anonymous chunk #" .. tostring(_anon_chunk), 1, #nomsu_code)
_anon_chunk = _anon_chunk + 1
end
version = version or nomsu_code:match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)")
local syntax_version = version and tonumber(version:match("^[0-9]+")) or Parser.version
local userdata = {
errors = { },
source = source,
comments = { }
}
local tree = Parser.patterns[syntax_version]:match(nomsu_code, nil, userdata)
if not tree or type(tree) == 'number' then
error("In file " .. tostring(colored.blue(tostring(source or "<unknown>"))) .. " failed to parse:\n" .. tostring(colored.onyellow(colored.black(nomsu_code))))
end
if next(userdata.errors) then
local keys
do
local _accum_0 = { }
local _len_0 = 1
for k, v in pairs(userdata.errors) do
_accum_0[_len_0] = k
_len_0 = _len_0 + 1
end
keys = _accum_0
end
table.sort(keys)
local errors
do
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #keys do
local k = keys[_index_0]
_accum_0[_len_0] = userdata.errors[k]
_len_0 = _len_0 + 1
end
errors = _accum_0
end
error("Errors occurred while parsing (v" .. tostring(syntax_version) .. "):\n\n" .. table.concat(errors, "\n\n"), 0)
end
local comments
do
local _accum_0 = { }
local _len_0 = 1
for p, c in pairs(userdata.comments) do
_accum_0[_len_0] = {
comment = c,
pos = p
}
_len_0 = _len_0 + 1
end
comments = _accum_0
end
table.sort(comments, function(a, b)
return a.pos > b.pos
end)
local comment_i = 1
local walk_tree
walk_tree = function(t)
local comment_buff = { }
while comments[#comments] and comments[#comments].pos <= t.source.start do
table.insert(comment_buff, table.remove(comments))
end
for _index_0 = 1, #t do
local x = t[_index_0]
if AST.is_syntax_tree(x) then
walk_tree(x)
end
end
while comments[#comments] and comments[#comments].pos <= t.source.stop do
table.insert(comment_buff, table.remove(comments))
end
if #comment_buff > 0 then
t.comments = comment_buff
end
end
walk_tree(tree)
return tree
end
Parser.is_operator = function(s) Parser.is_operator = function(s)
return not not (NOMSU_DEFS.operator_char ^ 1 * -1):match(s) return not not (NOMSU_DEFS.operator_char ^ 1 * -1):match(s)
end end

View File

@ -74,73 +74,7 @@ setmetatable(NOMSU_DEFS, {__index:(key)=>
return make_node return make_node
}) })
Parser = {version:3, patterns:{}} Parser = {version:4, patterns:{}}
do
-- Just for cleanliness, I put the language spec in its own file using a slightly modified
-- version of the lpeg.re syntax.
peg_tidier = re.compile [[
file <- %nl* {~ (def/comment) (%nl+ (def/comment))* %nl* ~}
def <- anon_def / captured_def
anon_def <- ({ident} (" "*) ":"
{~ ((%nl " "+ def_line?)+) / def_line ~}) -> "%1 <- %2"
captured_def <- ({ident} (" "*) "(" {ident} ")" (" "*) ":"
{~ ((%nl " "+ def_line?)+) / def_line ~}) -> "%1 <- (({} {| %3 |} {} %%userdata) -> %2)"
def_line <- (err / [^%nl])+
err <- ("(!!" { (!("!!)") .)* } "!!)") -> "(({} (%1) %%userdata) => error)"
ident <- [a-zA-Z_][a-zA-Z0-9_]*
comment <- "--" [^%nl]*
]]
for version=1,Parser.version
peg_file = io.open("nomsu.#{version}.peg")
if not peg_file and package.nomsupath
for path in package.nomsupath\gmatch("[^;]+")
peg_file = io.open(path.."/nomsu.#{version}.peg")
break if peg_file
assert(peg_file, "could not find nomsu .peg file")
nomsu_peg = peg_tidier\match(peg_file\read('*a'))
peg_file\close!
Parser.patterns[version] = re.compile(nomsu_peg, NOMSU_DEFS)
_anon_chunk = 0
Parser.parse = (nomsu_code, source=nil, version=nil)->
source or= nomsu_code.source
nomsu_code = tostring(nomsu_code)
unless source
source = Source("anonymous chunk ##{_anon_chunk}", 1, #nomsu_code)
_anon_chunk += 1
version or= nomsu_code\match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)")
syntax_version = version and tonumber(version\match("^[0-9]+")) or Parser.version
userdata = {
errors: {}, :source, comments: {}
}
tree = Parser.patterns[syntax_version]\match(nomsu_code, nil, userdata)
if not tree or type(tree) == 'number'
error "In file #{colored.blue tostring(source or "<unknown>")} failed to parse:\n#{colored.onyellow colored.black nomsu_code}"
if next(userdata.errors)
keys = [k for k,v in pairs(userdata.errors)]
table.sort(keys)
errors = [userdata.errors[k] for k in *keys]
error("Errors occurred while parsing (v#{syntax_version}):\n\n"..table.concat(errors, "\n\n"), 0)
comments = [{comment:c, pos:p} for p,c in pairs(userdata.comments)]
-- Sort in descending order so we can pop the first comments off the end one at a time
table.sort comments, (a,b)-> a.pos > b.pos
comment_i = 1
walk_tree = (t)->
export comment_i
comment_buff = {}
while comments[#comments] and comments[#comments].pos <= t.source.start
table.insert(comment_buff, table.remove(comments))
for x in *t
if AST.is_syntax_tree x
walk_tree x
while comments[#comments] and comments[#comments].pos <= t.source.stop
table.insert(comment_buff, table.remove(comments))
t.comments = comment_buff if #comment_buff > 0
walk_tree tree
return tree
Parser.is_operator = (s)-> Parser.is_operator = (s)->
return not not (NOMSU_DEFS.operator_char^1 * -1)\match(s) return not not (NOMSU_DEFS.operator_char^1 * -1)\match(s)

View File

@ -42,6 +42,7 @@ format_error = function(err)
err_msg = err_msg .. "\n\027[2m" .. tostring(fmt_str:format(err_linenum)) .. tostring(err_line) .. "\027[0m" err_msg = err_msg .. "\n\027[2m" .. tostring(fmt_str:format(err_linenum)) .. tostring(err_line) .. "\027[0m"
end end
local _, err_linenum_end, err_linepos_end = string2.line_at(err.source, err.stop) local _, err_linenum_end, err_linepos_end = string2.line_at(err.source, err.stop)
err_linenum_end = err_linenum_end or err_linenum
if err_linenum_end == err_linenum then if err_linenum_end == err_linenum then
err_msg = err_msg .. "\n" .. tostring(pointer) err_msg = err_msg .. "\n" .. tostring(pointer)
else else

View File

@ -34,6 +34,7 @@ format_error = (err)->
err_line = "\027[0m#{before}\027[41;30m#{during}#{nl_indicator}\027[0m#{after}" err_line = "\027[0m#{before}\027[41;30m#{during}#{nl_indicator}\027[0m#{after}"
err_msg ..= "\n\027[2m#{fmt_str\format(err_linenum)}#{err_line}\027[0m" err_msg ..= "\n\027[2m#{fmt_str\format(err_linenum)}#{err_line}\027[0m"
_, err_linenum_end, err_linepos_end = string2.line_at(err.source, err.stop) _, err_linenum_end, err_linepos_end = string2.line_at(err.source, err.stop)
err_linenum_end or= err_linenum
if err_linenum_end == err_linenum if err_linenum_end == err_linenum
err_msg ..= "\n#{pointer}" err_msg ..= "\n#{pointer}"
else else