From 778c125d195ef2baa49ffe8480cbe8e06dbe8979 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Thu, 1 Aug 2024 23:41:39 +0200 Subject: [PATCH] Improved performance --- src/build/compiler/Compile.go | 4 +- src/build/config/config.go | 3 - src/build/core/CompileAssert.go | 5 -- src/build/expression/Operator.go | 21 ++---- src/build/expression/Parse.go | 111 ++++++++++++++++--------------- src/build/scope/Stack.go | 1 + src/build/token/Kind.go | 16 ++--- src/build/token/Tokenize_test.go | 70 ++++++++++--------- src/cli/Run.go | 4 +- src/cli/System.go | 1 - 10 files changed, 112 insertions(+), 124 deletions(-) diff --git a/src/build/compiler/Compile.go b/src/build/compiler/Compile.go index a10224d..facd13b 100644 --- a/src/build/compiler/Compile.go +++ b/src/build/compiler/Compile.go @@ -11,8 +11,8 @@ import ( // Compile waits for the scan to finish and compiles all functions. func Compile(files <-chan *fs.File, functions <-chan *core.Function, errs <-chan error) (Result, error) { result := Result{} + allFiles := make([]*fs.File, 0, 8) allFunctions := map[string]*core.Function{} - allFiles := map[string]*fs.File{} for functions != nil || files != nil || errs != nil { select { @@ -31,7 +31,7 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, errs <-chan continue } - allFiles[file.Path] = file + allFiles = append(allFiles, file) case err, ok := <-errs: if !ok { diff --git a/src/build/config/config.go b/src/build/config/config.go index 7ff0e87..9254e64 100644 --- a/src/build/config/config.go +++ b/src/build/config/config.go @@ -23,7 +23,4 @@ var ( // Skips writing the executable to disk. Dry = false - - // Skips compiling assert statements. - SkipAsserts = false ) diff --git a/src/build/core/CompileAssert.go b/src/build/core/CompileAssert.go index 05cc38c..2715e44 100644 --- a/src/build/core/CompileAssert.go +++ b/src/build/core/CompileAssert.go @@ -5,15 +5,10 @@ import ( "git.akyoto.dev/cli/q/src/build/asm" "git.akyoto.dev/cli/q/src/build/ast" - "git.akyoto.dev/cli/q/src/build/config" ) // CompileAssert compiles an assertion. func (f *Function) CompileAssert(assert *ast.Assert) error { - if config.SkipAsserts { - return nil - } - f.count.assert++ success := fmt.Sprintf("%s_assert_%d_true", f.Name, f.count.assert) fail := fmt.Sprintf("%s_assert_%d_false", f.Name, f.count.assert) diff --git a/src/build/expression/Operator.go b/src/build/expression/Operator.go index 2a4ecdf..96431f1 100644 --- a/src/build/expression/Operator.go +++ b/src/build/expression/Operator.go @@ -15,7 +15,7 @@ type Operator struct { // Operators defines the operators used in the language. // The number corresponds to the operator priority and can not be zero. -var Operators = map[token.Kind]*Operator{ +var Operators = [64]Operator{ token.Period: {".", 13, 2}, token.Call: {"λ", 12, 1}, token.Array: {"@", 12, 2}, @@ -41,12 +41,15 @@ var Operators = map[token.Kind]*Operator{ token.LogicalAnd: {"&&", 2, 2}, token.LogicalOr: {"||", 1, 2}, + token.Separator: {",", 0, 2}, + token.Assign: {"=", math.MinInt8, 2}, token.Define: {":=", math.MinInt8, 2}, token.AddAssign: {"+=", math.MinInt8, 2}, token.SubAssign: {"-=", math.MinInt8, 2}, token.MulAssign: {"*=", math.MinInt8, 2}, token.DivAssign: {"/=", math.MinInt8, 2}, + token.ModAssign: {"%=", math.MinInt8, 2}, token.ShrAssign: {">>=", math.MinInt8, 2}, token.ShlAssign: {"<<=", math.MinInt8, 2}, } @@ -68,21 +71,9 @@ func isComplete(expr *Expression) bool { } func numOperands(symbol token.Kind) int { - operator, exists := Operators[symbol] - - if !exists { - return -1 - } - - return int(operator.Operands) + return int(Operators[symbol].Operands) } func precedence(symbol token.Kind) int8 { - operator, exists := Operators[symbol] - - if !exists { - return -1 - } - - return operator.Precedence + return Operators[symbol].Precedence } diff --git a/src/build/expression/Parse.go b/src/build/expression/Parse.go index 22dbd39..87e9768 100644 --- a/src/build/expression/Parse.go +++ b/src/build/expression/Parse.go @@ -101,63 +101,64 @@ func Parse(tokens []token.Token) *Expression { continue } - if t.IsOperator() { - if cursor == nil { - cursor = NewLeaf(t) - cursor.Precedence = precedence(t.Kind) - root = cursor - continue - } - - node := NewLeaf(t) - node.Precedence = precedence(t.Kind) - - if cursor.Token.IsOperator() { - oldPrecedence := cursor.Precedence - newPrecedence := node.Precedence - - if newPrecedence > oldPrecedence { - if len(cursor.Children) == numOperands(cursor.Token.Kind) { - cursor.LastChild().Replace(node) - } else { - cursor.AddChild(node) - } - } else { - start := cursor - - for start != nil { - precedence := start.Precedence - - if precedence < newPrecedence { - start.LastChild().Replace(node) - break - } - - if precedence == newPrecedence { - if start == root { - root = node - } - - start.Replace(node) - break - } - - start = start.Parent - } - - if start == nil { - root.Replace(node) - root = node - } - } - } else { - node.AddChild(cursor) - root = node - } - - cursor = node + if !t.IsOperator() { continue } + + if cursor == nil { + cursor = NewLeaf(t) + cursor.Precedence = precedence(t.Kind) + root = cursor + continue + } + + node := NewLeaf(t) + node.Precedence = precedence(t.Kind) + + if cursor.Token.IsOperator() { + oldPrecedence := cursor.Precedence + newPrecedence := node.Precedence + + if newPrecedence > oldPrecedence { + if len(cursor.Children) == numOperands(cursor.Token.Kind) { + cursor.LastChild().Replace(node) + } else { + cursor.AddChild(node) + } + } else { + start := cursor + + for start != nil { + precedence := start.Precedence + + if precedence < newPrecedence { + start.LastChild().Replace(node) + break + } + + if precedence == newPrecedence { + if start == root { + root = node + } + + start.Replace(node) + break + } + + start = start.Parent + } + + if start == nil { + root.Replace(node) + root = node + } + } + } else { + node.AddChild(cursor) + root = node + } + + cursor = node } return root diff --git a/src/build/scope/Stack.go b/src/build/scope/Stack.go index 40ce40a..ceca9e3 100644 --- a/src/build/scope/Stack.go +++ b/src/build/scope/Stack.go @@ -6,6 +6,7 @@ import ( "git.akyoto.dev/cli/q/src/build/token" ) +// Stack is a stack of scopes. type Stack struct { Scopes []*Scope } diff --git a/src/build/token/Kind.go b/src/build/token/Kind.go index 2c12464..6bbaff8 100644 --- a/src/build/token/Kind.go +++ b/src/build/token/Kind.go @@ -18,14 +18,6 @@ const ( BlockEnd // } ArrayStart // [ ArrayEnd // ] - _keywords // - Assert // assert - Else // else - If // if - Import // import - Loop // loop - Return // return - _keywordsEnd // _operators // Add // + Sub // - @@ -68,4 +60,12 @@ const ( ShrAssign // >>= _assignmentsEnd // _operatorsEnd // + _keywords // + Assert // assert + Else // else + If // if + Import // import + Loop // loop + Return // return + _keywordsEnd // ) diff --git a/src/build/token/Tokenize_test.go b/src/build/token/Tokenize_test.go index efc743c..4d6e36b 100644 --- a/src/build/token/Tokenize_test.go +++ b/src/build/token/Tokenize_test.go @@ -25,11 +25,15 @@ func TestFunction(t *testing.T) { } func TestKeyword(t *testing.T) { - tokens := token.Tokenize([]byte("return x")) + tokens := token.Tokenize([]byte("assert if import else loop return")) expected := []token.Kind{ + token.Assert, + token.If, + token.Import, + token.Else, + token.Loop, token.Return, - token.Identifier, token.EOF, } @@ -103,6 +107,37 @@ func TestOperator(t *testing.T) { } } +func TestOperatorAssign(t *testing.T) { + tokens := token.Tokenize([]byte(`a += b -= c *= d /= e &= f |= g ^= h <<= i >>= j`)) + + expected := []token.Kind{ + token.Identifier, + token.AddAssign, + token.Identifier, + token.SubAssign, + token.Identifier, + token.MulAssign, + token.Identifier, + token.DivAssign, + token.Identifier, + token.AndAssign, + token.Identifier, + token.OrAssign, + token.Identifier, + token.XorAssign, + token.Identifier, + token.ShlAssign, + token.Identifier, + token.ShrAssign, + token.Identifier, + token.EOF, + } + + for i, kind := range expected { + assert.Equal(t, tokens[i].Kind, kind) + } +} + func TestNegateFirstToken(t *testing.T) { tokens := token.Tokenize([]byte(`-a`)) @@ -244,37 +279,6 @@ func TestLeadingZero(t *testing.T) { } } -func TestOperatorAssign(t *testing.T) { - tokens := token.Tokenize([]byte(`a += b -= c *= d /= e &= f |= g ^= h <<= i >>= j`)) - - expected := []token.Kind{ - token.Identifier, - token.AddAssign, - token.Identifier, - token.SubAssign, - token.Identifier, - token.MulAssign, - token.Identifier, - token.DivAssign, - token.Identifier, - token.AndAssign, - token.Identifier, - token.OrAssign, - token.Identifier, - token.XorAssign, - token.Identifier, - token.ShlAssign, - token.Identifier, - token.ShrAssign, - token.Identifier, - token.EOF, - } - - for i, kind := range expected { - assert.Equal(t, tokens[i].Kind, kind) - } -} - func TestSeparator(t *testing.T) { tokens := token.Tokenize([]byte("a,b,c")) diff --git a/src/cli/Run.go b/src/cli/Run.go index 9133f36..7de865b 100644 --- a/src/cli/Run.go +++ b/src/cli/Run.go @@ -33,9 +33,9 @@ func Run(args []string) int { if err != nil { fmt.Fprintln(os.Stderr, err) - switch err.(type) { + switch err := err.(type) { case *exec.ExitError: - return 0 + return err.ExitCode() default: return 1 diff --git a/src/cli/System.go b/src/cli/System.go index 5a26b37..b523d7a 100644 --- a/src/cli/System.go +++ b/src/cli/System.go @@ -18,6 +18,5 @@ func System(args []string) int { fmt.Printf(line, "Compiler:", config.Executable) fmt.Printf(line, "Library:", config.Library) fmt.Printf(line, "Threads:", strconv.Itoa(runtime.NumCPU())) - return 0 }