From 9a957ccc181fb9559924918de5fed1e101ce52ca Mon Sep 17 00:00:00 2001 From: joe-conigliaro Date: Fri, 12 Jul 2019 15:37:54 +1000 Subject: [PATCH] module aliasing & file import scope --- compiler/cgen.v | 2 ++ compiler/main.v | 1 + compiler/parser.v | 40 ++++++++++++++++++++++----- compiler/scanner.v | 3 ++ compiler/table.v | 67 ++++++++++++++++++++++++++++++++++++++++++++- vlib/gg/gg.v | 2 ++ vlib/glm/glm_test.v | 1 + 7 files changed, 108 insertions(+), 8 deletions(-) diff --git a/compiler/cgen.v b/compiler/cgen.v index a946a4c843..a6e2edb5f9 100644 --- a/compiler/cgen.v +++ b/compiler/cgen.v @@ -4,6 +4,8 @@ module main +import os + struct CGen { out os.File out_path string diff --git a/compiler/main.v b/compiler/main.v index 0650c83896..8053745113 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -6,6 +6,7 @@ module main import os import time +import strings const ( Version = '0.1.14' diff --git a/compiler/parser.v b/compiler/parser.v index 3862324dfa..991cd65e6f 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -4,7 +4,9 @@ module main +import os import rand +import strings struct Var { mut: @@ -40,6 +42,7 @@ mut: lit string cgen *CGen table *Table + import_table *FileImportTable // Holds imports for just the file being parsed run Pass // TODO rename `run` to `pass` os OS mod string @@ -83,6 +86,7 @@ fn (c mut V) new_parser(path string, run Pass) Parser { file_name: path.all_after('/') scanner: new_scanner(path) table: c.table + import_table: new_file_import_table(path) cur_fn: EmptyFn cgen: c.cgen is_script: (c.pref.is_script && path == c.dir) @@ -132,7 +136,11 @@ fn (p mut Parser) parse() { p.builtin_pkg = p.mod == 'builtin' p.can_chash = p.mod == 'ft' || p.mod == 'http' || p.mod == 'glfw' || p.mod=='ui' // TODO tmp remove // Import pass - the first and the smallest pass that only analyzes imports - p.table.register_package(p.mod) + // fully qualify the module name, eg base64 to encoding.base64 + fq_mod := p.table.qualify_module(p.mod, p.file_path) + p.table.register_package(fq_mod) + // replace "." with "_" for C variable names + p.mod = fq_mod.replace('.', '_') if p.run == .imports { for p.tok == .key_import && p.peek() != .key_const { p.import_statement() @@ -284,6 +292,9 @@ fn (p mut Parser) import_statement() { for p.tok != .rpar && p.tok != .eof { pkg := p.lit.trim_space() p.next() + // TODO: aliased for import() syntax + // p.import_table.register_alias(alias, pkg) + // p.import_table.register_import(pkg) if p.table.imports.contains(pkg) { continue } @@ -297,20 +308,29 @@ fn (p mut Parser) import_statement() { if p.tok != .name { p.error('bad import format') } + // aliasing (import b64 encoding.base64) + mut alias := '' + if p.tok == .name && p.peek() == .name { + alias = p.check_name() + } mut pkg := p.lit.trim_space() // submodule support mut depth := 1 - p.next() + p.next() for p.tok == .dot { p.check(.dot) - submodule := p.check_name() + submodule := p.check_name() + if alias == '' { alias = submodule } pkg += '.' + submodule depth++ if depth > MaxModuleDepth { p.error('module depth of $MaxModuleDepth exceeded: $pkg') } } + if alias == '' { alias = pkg } p.fgenln(' ' + pkg) + // add import to file scope import table + p.import_table.register_alias(alias, pkg) // Make sure there are no duplicate imports if p.table.imports.contains(pkg) { return @@ -1283,9 +1303,14 @@ fn (p mut Parser) name_expr() string { // ////////////////////////// // module ? // Allow shadowing (gg = gg.newcontext(); gg.draw_triangle()) - if p.table.known_pkg(name) && !p.cur_fn.known_var(name) && !is_c { - // println('"$name" is a known pkg') - pkg := name + if ((name == p.mod && p.table.known_pkg(name)) || p.import_table.known_alias(name)) + && !p.cur_fn.known_var(name) && !is_c { + mut pkg := name + // must be aliased module + if name != p.mod && p.import_table.known_alias(name) { + // we replaced "." with "_" in p.mod for C variable names, do same here. + pkg = p.import_table.resolve_alias(name).replace('.', '_') + } p.next() p.check(.dot) name = p.lit @@ -1408,7 +1433,8 @@ fn (p mut Parser) name_expr() string { if !p.first_run() { // println('name_expr():') // If orig_name is a pkg, then printing undefined: `pkg` tells us nothing - if p.table.known_pkg(orig_name) { + // if p.table.known_pkg(orig_name) { + if p.table.known_pkg(orig_name) && p.import_table.known_alias(orig_name) { name = name.replace('__', '.') p.error('undefined: `$name`') } diff --git a/compiler/scanner.v b/compiler/scanner.v index 820905710c..b9255109a9 100644 --- a/compiler/scanner.v +++ b/compiler/scanner.v @@ -4,6 +4,9 @@ module main +import os +import strings + struct Scanner { mut: file_path string diff --git a/compiler/table.v b/compiler/table.v index 45c66e8f24..140e3de4b0 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -19,6 +19,13 @@ mut: obfuscate bool } +// Holds import information scoped to the parsed file +struct FileImportTable { +mut: + file_path string + imports map[string]string +} + enum AccessMod { private // private imkey_mut private_mut // private key_mut @@ -655,5 +662,63 @@ fn is_valid_int_const(val, typ string) bool { //return i64(-(1<<63)) <= x64 && x64 <= i64((1<<63)-1) } return true -} +} +// Once we have a module format we can read from module file instead +// this is not optimal +fn (table &Table) qualify_module(mod string, file_path string) string { + for m in table.imports { + if m.contains('.') && m.contains(mod) { + m_parts := m.split('.') + m_path := m_parts.join('/') + if mod == m_parts[m_parts.len-1] && file_path.contains(m_path) { + return m + } + } + } + return mod +} + +fn new_file_import_table(file_path string) *FileImportTable { + mut t := &FileImportTable{ + file_path: file_path + imports: map[string]string{} + } + return t +} + +fn (fit FileImportTable) known_import(mod string) bool { + return fit.imports.exists(mod) || fit.is_aliased(mod) +} + +fn (fit mut FileImportTable) register_import(mod string) { + fit.register_alias(mod, mod) +} + +fn (fit mut FileImportTable) register_alias(alias string, mod string) { + if !fit.imports.exists(alias) { + fit.imports[alias] = mod + } else { + panic('Cannot import $mod as $alias: import name $alias already in use in "${fit.file_path}".') + } +} + +fn (fit &FileImportTable) known_alias(alias string) bool { + return fit.imports.exists(alias) +} + +fn (fit &FileImportTable) is_aliased(mod string) bool { + for i in fit.imports.keys() { + if fit.imports[i] == mod { + return true + } + } + return false +} + +fn (fit &FileImportTable) resolve_alias(alias string) string { + if fit.imports.exists(alias) { + return fit.imports[alias] + } + return '' +} diff --git a/vlib/gg/gg.v b/vlib/gg/gg.v index 647cc1cf10..1bae019a75 100644 --- a/vlib/gg/gg.v +++ b/vlib/gg/gg.v @@ -7,6 +7,8 @@ module gg import stbi import glm import gl +import gx +import os struct Vec2 { x int diff --git a/vlib/glm/glm_test.v b/vlib/glm/glm_test.v index a1ff2bb1c6..8bcca9d733 100644 --- a/vlib/glm/glm_test.v +++ b/vlib/glm/glm_test.v @@ -5,6 +5,7 @@ import os import gl import gg +import glm fn cmp(a, b f32) bool { return int(a * 1000) == int(b * 1000)