X-Git-Url: http://nitlanguage.org diff --git a/src/parser_util.nit b/src/parser_util.nit index f9ea467..cd4f205 100644 --- a/src/parser_util.nit +++ b/src/parser_util.nit @@ -15,7 +15,7 @@ # Utils and tools related to parsers and AST module parser_util -import parser +intrude import parser import toolcontext redef class ToolContext @@ -30,7 +30,7 @@ redef class ToolContext var eof = tree.n_eof if eof isa AError then - self.fatal_error(null, "Fatal Error: {eof.message}") + self.fatal_error(null, "Fatal Error: {eof.message}.") abort end return tree.n_base.as(not null) @@ -43,7 +43,7 @@ redef class ToolContext var nmodule = parse_module(string) var nclassdefs = nmodule.n_classdefs if nclassdefs.length != 1 then - self.fatal_error(null, "Fatal Error: not a classdef") + self.fatal_error(null, "Fatal Error: not a classdef.") abort end return nclassdefs.first @@ -57,7 +57,7 @@ redef class ToolContext var nclassdef = parse_classdef(mod_string) var npropdefs = nclassdef.n_propdefs if npropdefs.length != 1 then - self.fatal_error(null, "Fatal Error: not a propdef") + self.fatal_error(null, "Fatal Error: not a propdef.") abort end return npropdefs.first @@ -69,7 +69,7 @@ redef class ToolContext do var mod_string = "do\n{string}\nend" var nmodule = parse_module(mod_string) - var nblock = nmodule.n_classdefs.first.n_propdefs.first.as(AMainMethPropdef).n_block.as(ABlockExpr).n_expr.first.as(ADoExpr).n_block.as(not null) + var nblock = nmodule.n_classdefs.first.n_propdefs.first.as(AMethPropdef).n_block.as(ABlockExpr).n_expr.first.as(ADoExpr).n_block.as(not null) return nblock end @@ -79,30 +79,175 @@ redef class ToolContext do var mod_string = "var dummy = \n{string}" var nmodule = parse_module(mod_string) - var nexpr = nmodule.n_classdefs.first.n_propdefs.first.as(AMainMethPropdef).n_block.as(ABlockExpr).n_expr.first.as(AVardeclExpr).n_expr.as(not null) + var nexpr = nmodule.n_classdefs.first.n_propdefs.first.as(AMethPropdef).n_block.as(ABlockExpr).n_expr.first.as(AVardeclExpr).n_expr.as(not null) return nexpr end -end -redef class ANode - # Return an array of tokens that match a given text - fun collect_tokens_by_text(text: String): Array[Token] + # Parse a super class declaration + # Fatal error if the `string` is not a syntactically correct super class declaration + fun parse_superclass(string: String): APropdef + do + var mod_string = "class Dummy\nsuper {string}\nend" + var nclassdef = parse_classdef(mod_string).as(AStdClassdef) + var nsuperclasses = nclassdef.n_propdefs + if nsuperclasses.length != 1 then + self.fatal_error(null, "Fatal Error: not a super class declaration.") + abort + end + return nsuperclasses.first + end + + # Try to parse the `string` as something + # + # Returns the first possible syntacticaly correct type among: + # + # - a type `AType` + # - a single `Token` + # - an expression `AExpr` + # - a block of statements `ABlockExpr` + # - a full module `AModule` + # - a `AError` if nothing else matches + # + # var tc = new ToolContext + # assert tc.parse_something("foo") isa TId + # assert tc.parse_something("foo[bar]") isa AExpr + # assert tc.parse_something("Foo[Bar]") isa AType + # assert tc.parse_something("foo\nbar") isa ABlockExpr + # assert tc.parse_something("fun foo do bar\nfoo") isa AModule + # assert tc.parse_something("fun fun") isa AParserError + # assert tc.parse_something("?%^&") isa ALexerError + fun parse_something(string: String): ANode + do + var source = new SourceFile.from_string("", string) + var error + var tree + var eof + var lexer + + lexer = new InjectedLexer(source) + lexer.injected_before.add new TKwvar + lexer.injected_before.add new TId + lexer.injected_before.add new TColumn + lexer.injected_before.add new TClassid + lexer.injected_before.add new TObra + lexer.injected_after.add new TCbra + tree = (new Parser(lexer)).parse + eof = tree.n_eof + if not eof isa AError then + var ntype = tree.n_base.n_classdefs.first.n_propdefs.first.as(AMethPropdef).n_block.as(ABlockExpr).n_expr.first.as(AVardeclExpr).n_type.n_types.first + ntype.parent = null + return ntype + end + error = eof + + lexer = new Lexer(source) + var first = lexer.next + if first isa EOF then return first + var second = lexer.next + if second isa EOF and not second isa AError then + first.parent = null + return first + end + + lexer = new InjectedLexer(source) + lexer.injected_before.add new TKwvar + lexer.injected_before.add new TId + lexer.injected_before.add new TAssign + lexer.injected_before.add new TOpar + lexer.injected_after.add new TCpar + tree = (new Parser(lexer)).parse + eof = tree.n_eof + if not eof isa AError then + var nexpr = tree.n_base.n_classdefs.first.n_propdefs.first.as(AMethPropdef).n_block.as(ABlockExpr).n_expr.first.as(AVardeclExpr).n_expr.as(AParExpr).n_expr + nexpr.parent = null + return nexpr + end + if eof.location > error.location then error = eof + + lexer = new InjectedLexer(source) + lexer.injected_before.add new TKwdo + lexer.injected_before.add new TEol + lexer.injected_after.add new TEol + lexer.injected_after.add new TKwend + tree = (new Parser(lexer)).parse + eof = tree.n_eof + if not eof isa AError then + var nblock = tree.n_base.n_classdefs.first.n_propdefs.first.as(AMethPropdef).n_block.as(ABlockExpr).n_expr.first.as(ADoExpr).n_block.as(ABlockExpr) + nblock.n_kwend = null # drop injected token + nblock.parent = null + return nblock + end + if eof.location > error.location then error = eof + + lexer = new Lexer(source) + tree = (new Parser(lexer)).parse + eof = tree.n_eof + if not eof isa AError then + return tree.n_base.as(not null) + end + if eof.location > error.location then error = eof + + return error + end + + # Parse the input of the user as something + fun interactive_parse(prompt: String): ANode do - var v = new CollectTokensByTextVisitor(text) - v.enter_visit(self) - return v.result + var oldtext = "" + + loop + printn prompt + printn " " + var s = sys.stdin.read_line + if s == "" then continue + if s.chars.first == ':' then + var res = new TString + res.text = s + return res + end + + var text = oldtext + s + "\n" + oldtext = "" + var n = parse_something(text) + + if n isa AParserError and n.token isa EOF then + # Unexpected end of file, thus continuing + if oldtext == "" then prompt = "." * prompt.length + oldtext = text + continue + end + + return n + end end end -private class CollectTokensByTextVisitor - super Visitor - var text: String - init(text: String) do self.text = text - var result = new Array[Token] - redef fun visit(node) +# A modified lexer that feed tokens before and after the real tokens. +class InjectedLexer + super Lexer + + # The tokens to use before the real tokens (in order). + var injected_before = new List[Token] + + # The tokens to use after the real tokens (in order). + # The real EOF token is produced after these tokens. + var injected_after = new List[Token] + private var is_finished = false + + redef fun get_token do - if node == null then return - node.visit_all(self) - if node isa Token and node.text == text then result.add(node) + if not injected_before.is_empty then + var tok = injected_before.shift + return tok + end + if not is_finished then + var next = super + if not next isa EOF then return next + injected_after.push(next) + is_finished = true + end + + var tok = injected_after.shift + return tok end end