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
}