Skip to content

Commit

Permalink
Released Lxa v0.2.4
Browse files Browse the repository at this point in the history
  • Loading branch information
BuildTools committed Mar 26, 2020
1 parent 31acfb2 commit 47c3866
Show file tree
Hide file tree
Showing 14 changed files with 215 additions and 83 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ WARNING! Please expect breaking changes and unstable APIs. Most of them are curr

## Update

* 2020/03/23 Relesed Lxa v0.1.0. Use Azure/golua as vm.
* 2020/03/23 Released Lxa v0.1.0. Use Azure/golua as vm.

* 2020/03/25 Relesed Lxa v0.2.1. Use Official lua 5.3.5 vm (written in c) as defualt vm. Added inner go lua vm as a option (several stdlib unsupported yet). Added Compile Only option to output compiled lua bytecode (since v0.2.0). Added linux support and x86 support (auto select static lib when compiling) (untested).
* 2020/03/25 Released Lxa v0.2.1. Use Official lua 5.3.5 vm (written in c) as defualt vm. Added inner go lua vm as a option (several stdlib unsupported yet). Added Compile Only option to output compiled lua bytecode (since v0.2.0). Added linux support and x86 support (auto select static lib when compiling) (untested).

* 2020/03/26 Released Lxa v0.2.4. Added Debug option for more details in running. Added Parse Only option for watch bytecode. Optimized Logical Expression generation, multiple `and` and `or` expressions are handled sepecially apart from Binary Opration Expression.

## Syntax

Expand All @@ -20,11 +22,11 @@ You can follow these examples or look EBNF below directly.

### Lexical Conventions

Basically the same as Lua.
Free-style Code, Basically the same as Lua.

Free-style Code, `;` is not necessary in the end of a sentence, but `\n` will be recognized as equal as `;`.
`;` is not necessary in the end of a sentence, `\n` will be recognized as equal as `;`.

But `&&`,`||`,`!` can be also used as `and`, `or`,`not`
`&&`,`||`,`!` can be also used as `and`, `or`,`not`

Removed `::`, `goto`, `repeat` ,`until` ,`do`,`elseif`,`end`,`then`

Expand Down Expand Up @@ -212,4 +214,4 @@ stat ::= ';'

## About

Contact me: E-mail: <gz@oasis.run>, QQ: <963796543>, WebSite: <http://www.oasis.run>
Contact me: E-mail: gz@oasis.run, QQ: 963796543, WebSite: http://www.oasis.run
7 changes: 7 additions & 0 deletions compiler/ast/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ func (exp *UnopExp) String() string {
exp.Op.Line, exp.Op, exp.Exp)
}

// (and | or) between expList
type LogicalExp struct {
NoBoolExpression
Op *Token
ExpList []Expression
}

// exp1 op exp2
type BinopExp struct {
NoBoolExpression
Expand Down
40 changes: 25 additions & 15 deletions compiler/generator/generate_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ func (fi *funcInfo) generateExpression(node Expression, a, n int) {
fi.generateTableConstructorExp(exp, a)
case *UnopExp:
fi.generateUnopExp(exp, a)
case *LogicalExp:
fi.generateLogicalExp(exp, a)
case *BinopExp:
fi.generateBinopExp(exp, a)
case *NameExp:
Expand Down Expand Up @@ -144,32 +146,40 @@ func (fi *funcInfo) generateUnopExp(node *UnopExp, a int) {
fi.usedRegs = oldRegs
}

// r[a] := exp1 op exp2
func (fi *funcInfo) generateBinopExp(node *BinopExp, a int) {
switch node.Op.Type {
case TOKEN_OP_AND, TOKEN_OP_OR:
oldRegs := fi.usedRegs
// r[a] := (and | or) between expList
func (fi *funcInfo) generateLogicalExp(node *LogicalExp, a int) {
var Jmps []int
oldRegs := fi.usedRegs

b, _ := fi.expToOpArg(node.Exp1, ARG_REG)
fi.usedRegs = oldRegs
b, _ := fi.expToOpArg(node.ExpList[0], ARG_REG)
fi.usedRegs = oldRegs
for _, exp := range node.ExpList[1:] {
if node.Op.Is(TOKEN_OP_AND) {
fi.emitTestSet(node.Op.Line, a, b, 0)
} else {
fi.emitTestSet(node.Op.Line, a, b, 1)
}
pcOfJmp := fi.emitJmp(node.Op.Line, 0, 0)
Jmps = append(Jmps, pcOfJmp)

b, _ = fi.expToOpArg(node.Exp2, ARG_REG)
b, _ = fi.expToOpArg(exp, ARG_REG)
fi.usedRegs = oldRegs
fi.emitMove(node.Op.Line, a, b)
}
for _, pcOfJmp := range Jmps {
fi.fixSbx(pcOfJmp, fi.pc()-pcOfJmp)
default:
oldRegs := fi.usedRegs
b, _ := fi.expToOpArg(node.Exp1, ARG_RK)
c, _ := fi.expToOpArg(node.Exp2, ARG_RK)
fi.emitBinaryOp(node.Op.Line, node.Op.Type, a, b, c)
fi.usedRegs = oldRegs
}
if b != a {
fi.emitMove(node.Op.Line, a, b)
}
}

// r[a] := exp1 op exp2
func (fi *funcInfo) generateBinopExp(node *BinopExp, a int) {
oldRegs := fi.usedRegs
b, _ := fi.expToOpArg(node.Exp1, ARG_RK)
c, _ := fi.expToOpArg(node.Exp2, ARG_RK)
fi.emitBinaryOp(node.Op.Line, node.Op.Type, a, b, c)
fi.usedRegs = oldRegs
}

// r[a] := name
Expand Down
64 changes: 52 additions & 12 deletions compiler/parser/optimizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,62 @@ func OptimizeLogicalQst(exp *UnopExp) Expression {
return exp
}

func OptimizeLogicalOr(exp *BinopExp) Expression {
if exp.Exp1.IsTrue() {
return exp.Exp1 // true or x => true
} else if exp.Exp1.IsFalse() && !isVarargOrFuncCall(exp.Exp2) {
return exp.Exp2 // false or x => x
func OptimizeLogicalOr(exp *LogicalExp) Expression {
if exp.ExpList[0].IsTrue() {
return exp.ExpList[0]
}

for i, e := range exp.ExpList { // true or x => true
if e.IsTrue() {
exp.ExpList = exp.ExpList[0 : i+1]
if len(exp.ExpList) == 1 {
return exp.ExpList[0]
} else {
return exp
}
}
}

for i := 0; i < len(exp.ExpList)-1; i++ { // false or x => x
if exp.ExpList[i].IsFalse() && !isVarargOrFuncCall(exp.ExpList[i+1]) {
exp.ExpList = append(exp.ExpList[0:i], exp.ExpList[i+1:]...)
}
}

if len(exp.ExpList) == 1 {
return exp.ExpList[0]
} else {
return exp
}
return exp
}

func OptimizeLogicalAnd(exp *BinopExp) Expression {
if exp.Exp1.IsFalse() {
return exp.Exp1 // false and x => false
} else if exp.Exp1.IsTrue() && !isVarargOrFuncCall(exp.Exp2) {
return exp.Exp2 // true and x => x
func OptimizeLogicalAnd(exp *LogicalExp) Expression {
if exp.ExpList[0].IsFalse() {
return exp.ExpList[0]
}

for i, e := range exp.ExpList { // false and x => false
if e.IsFalse() {
exp.ExpList = exp.ExpList[0 : i+1]
if len(exp.ExpList) == 1 {
return exp.ExpList[0]
} else {
return exp
}
}
}

for i := 0; i < len(exp.ExpList)-1; i++ { // true and x => x
if exp.ExpList[i].IsTrue() && !isVarargOrFuncCall(exp.ExpList[i+1]) {
exp.ExpList = append(exp.ExpList[0:i], exp.ExpList[i+1:]...)
}
}

if len(exp.ExpList) == 1 {
return exp.ExpList[0]
} else {
return exp
}
return exp
}

func OptimizeBitwiseBinaryOp(exp *BinopExp) Expression {
Expand Down
4 changes: 3 additions & 1 deletion compiler/parser/parse_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import (
)

func (p *Parser) parseBlock() *Block {
return &Block{
// Directly use return &Block{...} Here will cause a strange bug in delve.
block := &Block{
Statements: p.parseStatements(),
ReturnExps: p.parseReturnExps(),
LastLine: p.lexer.Line(),
}
return block
}

func (p *Parser) parseStatements() []Statement {
Expand Down
44 changes: 28 additions & 16 deletions compiler/parser/parse_expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,31 +56,43 @@ func (p *Parser) parseExp13() Expression {
// x ('||' | or) y
func (p *Parser) parseExp12() Expression {
exp := p.parseExp11()
if !p.lexer.PeekToken().Is(TOKEN_OP_OR) {
return exp
}

expList := []Expression{exp}
var op *Token
for p.lexer.PeekToken().Is(TOKEN_OP_OR) {
op := p.lexer.NextToken()
lor := &BinopExp{
Op: op,
Exp1: exp,
Exp2: p.parseExp11(),
}
exp = OptimizeLogicalOr(lor)
op = p.lexer.NextToken()
expList = append(expList, p.parseExp11())
}
return exp
lor := &LogicalExp{
Op: op,
ExpList: expList,
}

return OptimizeLogicalOr(lor)
}

// x ('&&' | and) y
func (p *Parser) parseExp11() Expression {
exp := p.parseExp10()
if !p.lexer.PeekToken().Is(TOKEN_OP_AND) {
return exp
}

expList := []Expression{exp}
var op *Token
for p.lexer.PeekToken().Is(TOKEN_OP_AND) {
op := p.lexer.NextToken()
land := &BinopExp{
Op: op,
Exp1: exp,
Exp2: p.parseExp10(),
}
exp = OptimizeLogicalAnd(land)
op = p.lexer.NextToken()
expList = append(expList, p.parseExp10())
}
return exp
land := &LogicalExp{
Op: op,
ExpList: expList,
}

return OptimizeLogicalAnd(land)
}

// compare
Expand Down
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
module lxa

go 1.14

require github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb
6 changes: 0 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +0,0 @@
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
35 changes: 24 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@ import (
)

var (
CLUA bool
GOLUA bool
//DEBUG bool
COMPILE bool
CLUA bool
GOLUA bool
DEBUG bool
PARSE bool
COMPILE bool
PROGNAME string
)

func init() {
PROGNAME = os.Args[0]
flag.BoolVar(&COMPILE, "c", false, "compile lxa file to lua bytecode")
//flag.BoolVar(&DEBUG, "g", false, "enable verbose logging and tracing")
flag.BoolVar(&DEBUG, "g", false, "enable verbose logging and tracing")
flag.BoolVar(&PARSE, "p", false, "parse and print lua bytecode only")
flag.BoolVar(&GOLUA, "golua", false, "use inner golua vm for excuting")
flag.BoolVar(&CLUA, "clua", false, "use inner official clua 5.3.5 vm for excuting")
flag.Parse()
Expand All @@ -32,6 +36,14 @@ func main() {
if err != nil {
fmt.Fprintf(os.Stderr, "reading %s: %v", filename, err)
}
if PARSE {
if binchunk.IsBinaryChunk(chunk) {
runner.ParseBinary(chunk)
} else {
fmt.Fprintf(os.Stderr, "parsinging %s: %s", filename, "is not a lua bytecode file")
}
continue
}
var data []byte
if binchunk.IsBinaryChunk(chunk) {
data = chunk
Expand All @@ -42,20 +54,21 @@ func main() {
if COMPILE {
ioutil.WriteFile(filename+".luac", data, 0666)
} else {
if GOLUA && !CLUA {
runner.GoRunBinary(data, filename)
if GOLUA && !CLUA || DEBUG {
runner.GoRunBinary(data, filename, DEBUG)
} else {
runner.CRunBinary(data, filename)
}
}
}
} else {
fmt.Println("Oops! No input files given.")
fmt.Println("Lxa 0.2.1 2020.03.25 Copyright (C) 2020 xaxys.")
fmt.Println("See more Details at github.com/xaxys/lxa.")
fmt.Println("Avaliable options are:")
fmt.Println("Lxa 0.2.4 2020.03.26 Copyright (C) 2020 xaxys.")
fmt.Println("usage:", PROGNAME, "[options] [script]")
fmt.Println("avaliable options are:")
fmt.Println(" -c ", "Compile a lxa file to lua bytecode without running")
//fmt.Println(" -g", "Enable verbose logging and tracing")
fmt.Println(" -g ", "Enable verbose logging and tracing (golua vm only)")
fmt.Println(" -p ", "Parse and Print lua bytecode without running")
fmt.Println(" -golua", "Use inner golua vm for excuting (several stdlib unsupported yet)")
fmt.Println(" -clua ", "Use inner official clua 5.3.5 vm for excuting (Default VM)")
}
Expand Down
20 changes: 10 additions & 10 deletions runner/clua_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package runner
/*
#ifdef _WIN32
#include<Windows.h>
#ifdef _WIN64
#cgo CFLAGS: -I ../lua-5.3.5_WIN64/include -std=gnu99
#cgo LDFLAGS: -L ../lua-5.3.5_WIN64 -static -llua53
#else
#cgo CFLAGS: -I ../lua-5.3.5_WIN32/include -std=gnu99
#cgo LDFLAGS: -L ../lua-5.3.5_WIN32 -static -llua53
#endif
#ifdef _WIN64
#cgo CFLAGS: -I ../lua-5.3.5_WIN64/include -std=gnu99
#cgo LDFLAGS: -L ../lua-5.3.5_WIN64 -static -llua53
#else
#cgo CFLAGS: -I ../lua-5.3.5_WIN32/include -std=gnu99
#cgo LDFLAGS: -L ../lua-5.3.5_WIN32 -static -llua53
#endif
#else
#cgo CFLAGS: -I ../lua-5.3.5_Linux/include -std=gnu99
#cgo LDFLAGS: -L ../lua-5.3.5_Linux -static -llua53
Expand Down Expand Up @@ -59,18 +59,18 @@ static int report (lua_State *L, int status) {
void run_binaryscript (const char *s, const int len, const char *pname) {
#ifdef _WIN32
SetConsoleOutputCP(65001);
SetConsoleOutputCP(65001); // utf8 output support
#endif
progname = pname;
lua_State *L = luaL_newstate();
if (L == NULL) {
lua_writestringerror("%s\n", "cannot create state: not enough memory");
l_message(progname, "cannot create state: not enough memory");
}
luaL_openlibs(L);
lua_pushcfunction(L, msghandler);
int status = luaL_loadbuffer(L, s, len, s);
if (status == LUA_OK) {
lua_pcall(L, 0, LUA_MULTRET, 0);
status = lua_pcall(L, 0, LUA_MULTRET, 0);
}
report(L, status);
lua_close(L);
Expand Down
Loading

0 comments on commit 47c3866

Please sign in to comment.