Skip to content

Commit c2e6c08

Browse files
committed
Allow multiple imports at once, fix enums
1 parent e78ed7c commit c2e6c08

5 files changed

Lines changed: 183 additions & 155 deletions

File tree

grammar.ebnf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ LetStmt = 'let' , identifier , '=' , Expression , ';' ;
3939
in the current scope, `as` can be used to import the target function under a different specified
4040
name.
4141
*)
42-
ImportStmt = 'import' , identifier , [ 'as' , identifier ] , 'from' , identifier , ';' ;
42+
ImportStmt = 'import' , ( identifier
43+
| '{' , identifier , { ',' , identifier } , [ ',' ] , '}' ) , 'from' , identifier
44+
, ';' ;
4345
4446
(*
4547
A <BreakStmt> is used to stop a loop entirely. It can optionally be followed by an expression that

homescript/analyzer.go

Lines changed: 62 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -487,13 +487,8 @@ func (self *Analyzer) visitImportStatement(node ImportStmt) (Result, *errors.Err
487487
}
488488
}
489489

490-
actualImport := node.Function
491-
if node.RewriteAs != nil {
492-
actualImport = *node.RewriteAs
493-
}
494-
495490
// Check if the function can be imported
496-
moduleCode, filename, exists, proceed, err := self.executor.ResolveModule(node.FromModule)
491+
moduleCode, filename, exists, shouldProceed, err := self.executor.ResolveModule(node.FromModule)
497492
if err != nil {
498493
self.issue(
499494
node.Range,
@@ -511,64 +506,74 @@ func (self *Analyzer) visitImportStatement(node ImportStmt) (Result, *errors.Err
511506
return Result{}, nil
512507
}
513508

514-
// Check if the function conflicts with existing values
515-
value := self.getVar(actualImport)
516-
function := makeFn(&actualImport, node.Range)
509+
if !shouldProceed {
510+
for _, imported := range node.Functions {
511+
function := makeFn(&imported, node.Range)
517512

518-
// Only report this non-critical error
519-
if value != nil {
520-
self.issue(node.Range,
521-
fmt.Sprintf("the name '%s' is already present in the current scope", actualImport),
522-
errors.ImportError,
523-
)
524-
return Result{}, nil
525-
} else {
526-
// Push a dummy function into the current scope
527-
self.addVar(actualImport, function, node.Range)
528-
// Add the funtion to the list of imported functions to avoid analysis
529-
self.getScope().importedFunctions = append(self.getScope().importedFunctions, actualImport)
530-
}
531-
532-
if !proceed {
533-
return Result{Value: &function}, nil
534-
}
535-
diagnostics, _, rootScope := Analyze(
536-
self.executor,
537-
moduleCode,
538-
make(map[string]Value),
539-
self.moduleStack,
540-
node.FromModule,
541-
filename,
542-
)
543-
moduleErrors := 0
544-
firstErrMessage := ""
545-
for _, diagnostic := range diagnostics {
546-
if diagnostic.Severity == Error {
547-
moduleErrors++
548-
if firstErrMessage == "" {
549-
firstErrMessage = diagnostic.Message
550-
}
513+
// Push a dummy function into the current scope
514+
self.addVar(imported, function, node.Range)
515+
// Add the funtion to the list of imported functions to avoid analysis
516+
self.getScope().importedFunctions = append(self.getScope().importedFunctions, imported)
551517
}
552-
}
553-
if moduleErrors > 0 {
554-
self.issue(
555-
node.Range,
556-
fmt.Sprintf("target module contains %d error(s): %s", moduleErrors, firstErrMessage),
557-
errors.ImportError,
558-
)
559518
return Result{}, nil
560519
}
561-
functionValue, found := rootScope[node.Function]
562-
if !found {
563-
self.issue(
564-
node.Range,
565-
fmt.Sprintf("no function named '%s' found in module '%s'", node.Function, node.FromModule),
566-
errors.ImportError,
520+
521+
for _, imported := range node.Functions {
522+
value := self.getVar(imported)
523+
function := makeFn(&imported, node.Range)
524+
525+
// Only report this non-critical error
526+
if value != nil {
527+
self.issue(node.Range,
528+
fmt.Sprintf("the name '%s' is already present in the current scope", imported),
529+
errors.ImportError,
530+
)
531+
return Result{}, nil
532+
} else {
533+
// Push a dummy function into the current scope
534+
self.addVar(imported, function, node.Range)
535+
// Add the funtion to the list of imported functions to avoid analysis
536+
self.getScope().importedFunctions = append(self.getScope().importedFunctions, imported)
537+
}
538+
539+
diagnostics, _, rootScope := Analyze(
540+
self.executor,
541+
moduleCode,
542+
make(map[string]Value),
543+
self.moduleStack,
544+
node.FromModule,
545+
filename,
567546
)
568-
return Result{}, nil
547+
moduleErrors := 0
548+
firstErrMessage := ""
549+
for _, diagnostic := range diagnostics {
550+
if diagnostic.Severity == Error {
551+
moduleErrors++
552+
if firstErrMessage == "" {
553+
firstErrMessage = diagnostic.Message
554+
}
555+
}
556+
}
557+
if moduleErrors > 0 {
558+
self.issue(
559+
node.Range,
560+
fmt.Sprintf("target module contains %d error(s): %s", moduleErrors, firstErrMessage),
561+
errors.ImportError,
562+
)
563+
return Result{}, nil
564+
}
565+
_, found := rootScope[imported]
566+
if !found {
567+
self.issue(
568+
node.Range,
569+
fmt.Sprintf("no function named `%s` found in module `%s`", imported, node.FromModule),
570+
errors.ImportError,
571+
)
572+
return Result{}, nil
573+
}
569574
}
570575

571-
return Result{Value: functionValue}, nil
576+
return Result{}, nil
572577
}
573578

574579
func (self *Analyzer) visitBreakStatement(node BreakStmt) (Result, *errors.Error) {

homescript/interpreter.go

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -278,38 +278,37 @@ func (self *Interpreter) visitImportStatement(node ImportStmt) (Result, *int, *e
278278
}
279279
}
280280
// Resolve the function to be imported
281-
function, rootScope, err := self.ResolveModule(
281+
functions, rootScope, err := self.ResolveModule(
282282
node.Span(),
283283
node.FromModule,
284-
node.Function,
284+
node.Functions,
285285
)
286286
if err != nil {
287287
return Result{}, nil, err
288288
}
289-
actualImport := node.Function
290-
if node.RewriteAs != nil {
291-
actualImport = *node.RewriteAs
292-
}
293289

294-
// Check if the function conflicts with existing values
295-
value := self.getVar(actualImport)
296-
if value != nil {
297-
return Result{}, nil, errors.NewError(
298-
node.Span(),
299-
fmt.Sprintf("the name '%s' is already present in the current scope", actualImport),
300-
errors.ImportError,
301-
)
302-
}
290+
for _, function := range functions {
291+
// Check if the function conflicts with existing values
292+
value := self.getVar(*(*function).(ValueFunction).Identifier)
293+
if value != nil {
294+
return Result{}, nil, errors.NewError(
295+
node.Span(),
296+
fmt.Sprintf("the name `%s` is already present in the current scope", *(*function).(ValueFunction).Identifier),
297+
errors.ImportError,
298+
)
299+
}
303300

304-
// Push the function into the current scope
305-
self.addVar(actualImport, function)
301+
// Push the function into the current scope
302+
self.addVar(*(*function).(ValueFunction).Identifier, function)
306303

307-
// Migrate scope from import
308-
for key, value := range rootScope {
309-
self.addVar(fmt.Sprintf("%s::%s", node.FromModule, key), value)
304+
// Migrate scope from import
305+
for key, value := range rootScope {
306+
self.addVar(fmt.Sprintf("%s::%s", node.FromModule, key), value)
307+
}
310308
}
311309

312-
return Result{Value: function}, nil, nil
310+
null := makeNull(node.Span())
311+
return Result{Value: &null}, nil, nil
313312
}
314313

315314
func (self *Interpreter) visitBreakStatement(node BreakStmt) (Result, *int, *errors.Error) {
@@ -1579,11 +1578,11 @@ func (self *Interpreter) getVar(key string) *Value {
15791578
return nil
15801579
}
15811580

1582-
// Resolves a function imported by an 'import' statement
1581+
// Resolves functions imported by an 'import' statement
15831582
// The builtin just has the task of providing the target module code
1584-
// This function then runs the target module code and returns the value of the target function (analyzes the root scope)
1583+
// This function then runs the target module code and returns the values of the target functionsj (analyzes the root scope)
15851584
// If the target module contains top level code, it is also executed
1586-
func (self Interpreter) ResolveModule(span errors.Span, module string, function string) (*Value, map[string]*Value, *errors.Error) {
1585+
func (self Interpreter) ResolveModule(span errors.Span, module string, functions []string) ([]*Value, map[string]*Value, *errors.Error) {
15871586
moduleCode, filename, found, _, err := self.executor.ResolveModule(module)
15881587
if err != nil {
15891588
return nil, nil, errors.NewError(
@@ -1609,7 +1608,7 @@ func (self Interpreter) ResolveModule(span errors.Span, module string, function
16091608
false,
16101609
10,
16111610
self.moduleStack,
1612-
function,
1611+
"import",
16131612
filename,
16141613
)
16151614
if len(runErr) > 0 {
@@ -1627,13 +1626,20 @@ func (self Interpreter) ResolveModule(span errors.Span, module string, function
16271626
errors.ImportError,
16281627
)
16291628
}
1630-
functionValue, found := rootScope[function]
1631-
if !found {
1632-
return nil, nil, errors.NewError(
1633-
span,
1634-
fmt.Sprintf("no function named '%s' found in module '%s'", function, module),
1635-
errors.ImportError,
1636-
)
1629+
1630+
resultFunctions := make([]*Value, 0)
1631+
for _, function := range functions {
1632+
1633+
functionValue, found := rootScope[function]
1634+
if !found {
1635+
return nil, nil, errors.NewError(
1636+
span,
1637+
fmt.Sprintf("no function named '%s' found in module '%s'", function, module),
1638+
errors.ImportError,
1639+
)
1640+
}
1641+
resultFunctions = append(resultFunctions, functionValue)
16371642
}
1638-
return functionValue, rootScope, nil
1643+
1644+
return resultFunctions, rootScope, nil
16391645
}

homescript/node.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,8 @@ func (self LetStmt) Kind() StatementKind { return LetStmtKind }
9292
func (self LetStmt) Span() errors.Span { return self.Range }
9393

9494
type ImportStmt struct {
95-
Function string // import `foo`
96-
RewriteAs *string // as `bar`
97-
FromModule string // from `baz`
95+
Functions []string // import `foo`
96+
FromModule string // from `baz`
9897
Range errors.Span
9998
}
10099

0 commit comments

Comments
 (0)