diff --git a/lib/rivet/src/parser/import.ri b/lib/rivet/src/parser/import.ri new file mode 100644 index 000000000..2937f01ae --- /dev/null +++ b/lib/rivet/src/parser/import.ri @@ -0,0 +1,163 @@ +// Copyright (C) 2023-present Jose Mendoza - All rights reserved. Use of this +// source code is governed by an MIT license that can be found in the LICENSE +// file. + +import std/strings; + +import ../ast; +import ../token; +import ../report; + +extend Parser { + #[inline] + func parse_import_decl(mut self, attributes: ast.Attributes, is_public: bool, mut pos: token.Pos) -> ast.Decl { + return .Import(self.parse_import("", attributes, is_public, pos)); + } + + func parse_import( + mut self, prev_mod_path: string, attributes: ast.Attributes, is_public: bool, mut pos: token.Pos + ) -> ast.ImportDecl { + mut has_custom_alias := false; + mut mod_path_pos := self.tok.pos; + mut mod_path := self.parse_import_path(); + if !prev_mod_path.is_empty() { + mod_path = "{}/{}".fmt(prev_mod_path, mod_path); + } + mod_path_pos += self.prev_tok.pos; + mut subimports := []mut ast.ImportDecl(); + if self.tok.kind == .Div && self.peek_tok.kind == .Lbrace { + self.advance(2); + while { + mut subimport_pos := self.tok.pos; + subimports.push(self.parse_import(mod_path, attributes, is_public, subimport_pos)); + if !self.accept(.Comma) { + break; + } + } + self.expect(.Rbrace); + } + (glob, import_list) := self.parse_import_list(mod_path_pos); + alias_name := if subimports.is_empty() && import_list.is_empty() && self.accept(.KwAs) { + has_custom_alias = true; + new_name := self.parse_name(); + mod_path_pos += self.prev_tok.pos; + new_name + } else { + "" + }; + pos += self.prev_tok.pos; + if prev_mod_path.is_empty() { + self.expect(.Semicolon); + } + return ast.ImportDecl( + attributes: attributes, + is_public: is_public, + path: mod_path, + path_pos: mod_path_pos, + alias_name: alias_name, + has_custom_alias: has_custom_alias, + glob: glob, + subimports: subimports, + import_list: import_list, + pos: pos + ); + } + + func parse_import_path(mut self) -> string { + mut sb := strings.Builder.new(); + if self.accept(.Dot) { + sb.write_string("./"); + self.expect(.Div); + } else { + while self.accept(.DotDot) { + sb.write_string("../"); + self.expect(.Div); + } + } + sb.write_string(self.parse_name()); + while self.tok.kind == .Div && self.peek_tok.kind != .Lbrace { + self.expect(.Div); + sb.write_string("/"); + sb.write_string(self.parse_name()); + } + return sb.to_string(); + } + + func parse_import_list(mut self, mod_path_pos: token.Pos) -> (bool, []ast.ImportListInfo) { + mut import_list := []ast.ImportListInfo(); + mut glob := false; + if self.accept(.Dot) { + if self.accept(.Lbrace) { + mut lbrace_pos := self.prev_tok.pos; + while { + info_pos := self.tok.pos; + mut info_has_custom_alias := false; + match { + self.accept(.KwSelf) -> { + name := "self"; + info_alias := if self.accept(.KwAs) { + info_has_custom_alias = true; + self.parse_name() + } else { + name + }; + import_list.push( + ast.ImportListInfo( + name, info_alias, info_has_custom_alias, info_pos + ) + ); + }, + else -> { + name := self.parse_name(); + info_alias := if self.accept(.KwAs) { + info_has_custom_alias = true; + self.parse_name() + } else { + name + }; + import_list.push( + ast.ImportListInfo( + name, info_alias, info_has_custom_alias, info_pos + ) + ); + } + } + if !self.accept(.Comma) { + break; + } + } + lbrace_pos += self.tok.pos; + self.expect(.Rbrace); + if import_list.len == 1 { + mut warn := report.warn_builder("unnecessary use of braces", lbrace_pos); + warn.add_help("consider removing those braces"); + warn.emit(); + } + } else if self.tok.kind == .Name { + info_pos := self.tok.pos; + name := self.parse_name(); + mut info_has_custom_alias := false; + info_alias := if self.accept(.KwAs) { + info_has_custom_alias = true; + self.parse_name() + } else { + name + }; + import_list.push( + ast.ImportListInfo( + name, info_alias, info_has_custom_alias, info_pos + ) + ); + } else if self.accept(.Mul) { + glob = true; + } else { + mut err := report.error_builder( + "invalid syntax for unqualified import", mod_path_pos + ); + err.add_note("expected a single name, a list of names or `*`"); + err.emit(); + } + } + return (glob, import_list); + } +} \ No newline at end of file