Skip to content

Commit

Permalink
Major refactor, tree shaking
Browse files Browse the repository at this point in the history
  • Loading branch information
Vilsol committed Dec 29, 2020
1 parent e15156a commit a91d432
Show file tree
Hide file tree
Showing 13 changed files with 1,045 additions and 926 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ A Web IDE is available [here](https://vilsol.github.io/go-mlog-web/?1)
* Binary and Unary math
* Function level variable scopes
* Contextual errors
* Tree-shaking unused functions
* Multi-pass pre/post-processing

## Roadmap

* Full variable block scoping
* Variable argument count functions
* Multiple function return values
* Optimize simple jump instructions
* Multi-pass post-processing
* Tree-shake unused functions

## Design Limitations

Expand Down
29 changes: 29 additions & 0 deletions tests/function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,35 @@ read @counter bank1 @stack
op add @stack @stack 1
write 8 bank1 @stack
jump 2 always
op sub @stack @stack 1`,
},
{
name: "TreeShake",
input: `package main
func main() {
hello()
}
func hello() {
println("hello")
}
func foo() {
println("foo")
}
func bar() {
println("bar")
}`,
output: `set @stack 0
jump 5 always
print "hello"
print "\n"
read @counter bank1 @stack
op add @stack @stack 1
write 8 bank1 @stack
jump 2 always
op sub @stack @stack 1`,
},
}
Expand Down
4 changes: 2 additions & 2 deletions tests/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ op sub @stack @stack 2 // Update Stack Pointer
set _main_0 @return // Set the variable to the value
print _main_0 // Call to native function
print "\n" // Call to native function
op add _main_i _main_i 1 // Execute for loop post condition increment/decrement
op add _main_i _main_i 1 // Execute increment/decrement
jump 9 lessThan _main_i 10 // Jump to start of loop`,
},
{
Expand Down Expand Up @@ -174,7 +174,7 @@ jump 9 lessThan _main_i 10 // Jump to start of loop`,
15: set _main_0 @return // Set the variable to the value
16: print _main_0 // Call to native function
17: print "\n" // Call to native function
18: op add _main_i _main_i 1 // Execute for loop post condition increment/decrement
18: op add _main_i _main_i 1 // Execute increment/decrement
19: jump 9 lessThan _main_i 10 // Jump to start of loop`,
},
}
Expand Down
255 changes: 131 additions & 124 deletions transpiler/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,137 +11,23 @@ import (
func expressionToMLOG(ctx context.Context, ident []Resolvable, expr ast.Expr) ([]MLOGStatement, error) {
switch castExpr := expr.(type) {
case *ast.BasicLit:
value := castExpr.Value
if castExpr.Kind == token.CHAR {
value = "\"" + strings.Trim(value, "'") + "\""
}

return []MLOGStatement{&MLOG{
Comment: "Set the variable to the value",
Statement: [][]Resolvable{
{
&Value{Value: "set"},
ident[0],
&Value{Value: value},
},
},
}}, nil
return basicLitToMLOG(ctx, ident, castExpr)
case *ast.Ident:
if castExpr.Name == "true" || castExpr.Name == "false" {
return []MLOGStatement{&MLOG{
Comment: "Set the variable to the value",
Statement: [][]Resolvable{
{
&Value{Value: "set"},
ident[0],
&Value{Value: castExpr.Name},
},
},
}}, nil
}

return []MLOGStatement{&MLOG{
Comment: "Set the variable to the value of other variable",
Statement: [][]Resolvable{
{
&Value{Value: "set"},
ident[0],
&NormalVariable{Name: castExpr.Name},
},
},
}}, nil
return identToMLOG(ctx, ident, castExpr)
case *ast.BinaryExpr:
if opTranslated, ok := regularOperators[castExpr.Op]; ok {
instructions := make([]MLOGStatement, 0)

leftSide, leftExprInstructions, err := exprToResolvable(ctx, castExpr.X)
if err != nil {
return nil, err
}
instructions = append(instructions, leftExprInstructions...)

rightSide, rightExprInstructions, err := exprToResolvable(ctx, castExpr.Y)
if err != nil {
return nil, err
}
instructions = append(instructions, rightExprInstructions...)

return append(instructions, &MLOG{
Comment: "Execute operation",
Statement: [][]Resolvable{
{
&Value{Value: "op"},
&Value{Value: opTranslated},
ident[0],
leftSide,
rightSide,
},
},
}), nil
} else {
return nil, Err(ctx, fmt.Sprintf("operator statement cannot use this operation: %s", castExpr.Op.String()))
}
return binaryExprToMLOG(ctx, ident, castExpr)
case *ast.CallExpr:
callInstructions, err := callExprToMLOG(ctx, castExpr, ident)
if err != nil {
return nil, err
}
return callInstructions, err
return callExprToMLOG(ctx, castExpr, ident)
case *ast.UnaryExpr:
if _, ok := regularOperators[castExpr.Op]; ok {
instructions := make([]MLOGStatement, 0)

x, exprInstructions, err := exprToResolvable(ctx, castExpr.X)
if err != nil {
return nil, err
}
instructions = append(instructions, exprInstructions...)

var statement []Resolvable
switch castExpr.Op {
case token.NOT:
statement = []Resolvable{
&Value{Value: "op"},
&Value{Value: regularOperators[token.NOT]},
ident[0],
x,
&Value{Value: "-1"},
}
break
case token.SUB:
statement = []Resolvable{
&Value{Value: "op"},
&Value{Value: regularOperators[token.MUL]},
ident[0],
x,
&Value{Value: "-1"},
}
break
}

if statement == nil {
return nil, Err(ctx, fmt.Sprintf("unsupported unary operation: %s", castExpr.Op.String()))
}

return append(instructions, &MLOG{
Comment: "Execute unary operation",
Statement: [][]Resolvable{statement},
}), nil
} else {
return nil, Err(ctx, fmt.Sprintf("operator statement cannot use this operation: %s", castExpr.Op.String()))
}
return unaryExprToMLOG(ctx, ident, castExpr)
case *ast.ParenExpr:
instructions, err := expressionToMLOG(ctx, ident, castExpr.X)
if err != nil {
return nil, err
}
return instructions, nil
return expressionToMLOG(ctx, ident, castExpr.X)
case *ast.SelectorExpr:
mlog, _, err := selectorExprToMLOG(ctx, ident[0], castExpr)
return mlog, err
default:
return nil, Err(ctx, fmt.Sprintf("unsupported expression type: %T", expr))
}

return nil, Err(ctx, fmt.Sprintf("unsupported expression type: %T", expr))
}

func exprToResolvable(ctx context.Context, expr ast.Expr) (Resolvable, []MLOGStatement, error) {
Expand All @@ -163,10 +49,9 @@ func exprToResolvable(ctx context.Context, expr ast.Expr) (Resolvable, []MLOGSta
}

return dVar, exprInstructions, nil
default:
return nil, nil, Err(ctx, fmt.Sprintf("unknown resolvable expression type: %T", expr))
}

return nil, nil, Err(ctx, fmt.Sprintf("unknown resolvable expression type: %T", expr))
}

func selectorExprToMLOG(ctx context.Context, ident Resolvable, selectorExpr *ast.SelectorExpr) ([]MLOGStatement, string, error) {
Expand Down Expand Up @@ -323,3 +208,125 @@ func argumentsToResolvables(ctx context.Context, args []ast.Expr) ([]Resolvable,

return result, instructions, nil
}

func unaryExprToMLOG(ctx context.Context, ident []Resolvable, expr *ast.UnaryExpr) ([]MLOGStatement, error) {
if _, ok := regularOperators[expr.Op]; ok {
instructions := make([]MLOGStatement, 0)

x, exprInstructions, err := exprToResolvable(ctx, expr.X)
if err != nil {
return nil, err
}
instructions = append(instructions, exprInstructions...)

var statement []Resolvable
switch expr.Op {
case token.NOT:
statement = []Resolvable{
&Value{Value: "op"},
&Value{Value: regularOperators[token.NOT]},
ident[0],
x,
&Value{Value: "-1"},
}
break
case token.SUB:
statement = []Resolvable{
&Value{Value: "op"},
&Value{Value: regularOperators[token.MUL]},
ident[0],
x,
&Value{Value: "-1"},
}
break
}

if statement == nil {
return nil, Err(ctx, fmt.Sprintf("unsupported unary operation: %s", expr.Op.String()))
}

return append(instructions, &MLOG{
Comment: "Execute unary operation",
Statement: [][]Resolvable{statement},
}), nil
}

return nil, Err(ctx, fmt.Sprintf("operator statement cannot use this operation: %s", expr.Op.String()))
}

func binaryExprToMLOG(ctx context.Context, ident []Resolvable, expr *ast.BinaryExpr) ([]MLOGStatement, error) {
if opTranslated, ok := regularOperators[expr.Op]; ok {
instructions := make([]MLOGStatement, 0)

leftSide, leftExprInstructions, err := exprToResolvable(ctx, expr.X)
if err != nil {
return nil, err
}
instructions = append(instructions, leftExprInstructions...)

rightSide, rightExprInstructions, err := exprToResolvable(ctx, expr.Y)
if err != nil {
return nil, err
}
instructions = append(instructions, rightExprInstructions...)

return append(instructions, &MLOG{
Comment: "Execute operation",
Statement: [][]Resolvable{
{
&Value{Value: "op"},
&Value{Value: opTranslated},
ident[0],
leftSide,
rightSide,
},
},
}), nil
}

return nil, Err(ctx, fmt.Sprintf("operator statement cannot use this operation: %s", expr.Op.String()))
}

func identToMLOG(_ context.Context, ident []Resolvable, expr *ast.Ident) ([]MLOGStatement, error) {
if expr.Name == "true" || expr.Name == "false" {
return []MLOGStatement{&MLOG{
Comment: "Set the variable to the value",
Statement: [][]Resolvable{
{
&Value{Value: "set"},
ident[0],
&Value{Value: expr.Name},
},
},
}}, nil
}

return []MLOGStatement{&MLOG{
Comment: "Set the variable to the value of other variable",
Statement: [][]Resolvable{
{
&Value{Value: "set"},
ident[0],
&NormalVariable{Name: expr.Name},
},
},
}}, nil
}

func basicLitToMLOG(_ context.Context, ident []Resolvable, expr *ast.BasicLit) ([]MLOGStatement, error) {
value := expr.Value
if expr.Kind == token.CHAR {
value = "\"" + strings.Trim(value, "'") + "\""
}

return []MLOGStatement{&MLOG{
Comment: "Set the variable to the value",
Statement: [][]Resolvable{
{
&Value{Value: "set"},
ident[0],
&Value{Value: value},
},
},
}}, nil
}
Loading

0 comments on commit a91d432

Please sign in to comment.