Added compiler context.

This commit is contained in:
Marvin Blum
2015-10-28 18:38:49 +01:00
parent 53ea74a19b
commit 393ddd4326
4 changed files with 334 additions and 330 deletions

View File

@@ -6,350 +6,350 @@ import (
// Parses tokens, validates code to a specific degree // Parses tokens, validates code to a specific degree
// and writes SQF code into desired location. // and writes SQF code into desired location.
func Parse(token []Token, prettyPrinting bool) string { func (c *Compiler) Parse(token []Token, prettyPrinting bool) string {
if !initParser(token, prettyPrinting) { if !c.initParser(token, prettyPrinting) {
return "" return ""
} }
for tokenIndex < len(token) { for c.tokenIndex < len(token) {
parseBlock() c.parseBlock()
} }
return out return c.out
} }
func parseBlock() { func (c *Compiler) parseBlock() {
if accept("var") { if c.accept("var") {
parseVar() c.parseVar()
} else if accept("if") { } else if c.accept("if") {
parseIf() c.parseIf()
} else if accept("while") { } else if c.accept("while") {
parseWhile() c.parseWhile()
} else if accept("switch") { } else if c.accept("switch") {
parseSwitch() c.parseSwitch()
} else if accept("for") { } else if c.accept("for") {
parseFor() c.parseFor()
} else if accept("foreach") { } else if c.accept("foreach") {
parseForeach() c.parseForeach()
} else if accept("func") { } else if c.accept("func") {
parseFunction() c.parseFunction()
} else if accept("return") { } else if c.accept("return") {
parseReturn() c.parseReturn()
} else if accept("try") { } else if c.accept("try") {
parseTryCatch() c.parseTryCatch()
} else if accept("exitwith") { } else if c.accept("exitwith") {
parseExitWith() c.parseExitWith()
} else if accept("waituntil") { } else if c.accept("waituntil") {
parseWaitUntil() c.parseWaitUntil()
} else if accept("case") || accept("default") { } else if c.accept("case") || c.accept("default") {
return return
} else { } else {
parseStatement() c.parseStatement()
} }
if !end() && !accept("}") { if !c.end() && !c.accept("}") {
parseBlock() c.parseBlock()
} }
} }
func parseVar() { func (c *Compiler) parseVar() {
expect("var") c.expect("var")
appendOut(get().token, false) c.appendOut(c.get().token, false)
next() c.next()
if accept("=") { if c.accept("=") {
next() c.next()
appendOut(" = ", false) c.appendOut(" = ", false)
if accept("[") { if c.accept("[") {
parseArray() c.parseArray()
} else { } else {
parseExpression(true) c.parseExpression(true)
} }
} }
expect(";") c.expect(";")
appendOut(";", true) c.appendOut(";", true)
} }
func parseArray() { func (c *Compiler) parseArray() {
expect("[") c.expect("[")
appendOut("[", false) c.appendOut("[", false)
if !accept("]") { if !c.accept("]") {
parseExpression(true) c.parseExpression(true)
for accept(",") { for c.accept(",") {
next() c.next()
appendOut(",", false) c.appendOut(",", false)
parseExpression(true) c.parseExpression(true)
} }
} }
expect("]") c.expect("]")
appendOut("]", false) c.appendOut("]", false)
} }
func parseIf() { func (c *Compiler) parseIf() {
expect("if") c.expect("if")
appendOut("if (", false) c.appendOut("if (", false)
parseExpression(true) c.parseExpression(true)
appendOut(") then {", true) c.appendOut(") then {", true)
expect("{") c.expect("{")
parseBlock() c.parseBlock()
expect("}") c.expect("}")
if accept("else") { if c.accept("else") {
next() c.next()
expect("{") c.expect("{")
appendOut("} else {", true) c.appendOut("} else {", true)
parseBlock() c.parseBlock()
expect("}") c.expect("}")
} }
appendOut("};", true) c.appendOut("};", true)
} }
func parseWhile() { func (c *Compiler) parseWhile() {
expect("while") c.expect("while")
appendOut("while {", false) c.appendOut("while {", false)
parseExpression(true) c.parseExpression(true)
appendOut("} do {", true) c.appendOut("} do {", true)
expect("{") c.expect("{")
parseBlock() c.parseBlock()
expect("}") c.expect("}")
appendOut("};", false) c.appendOut("};", false)
} }
func parseSwitch() { func (c *Compiler) parseSwitch() {
expect("switch") c.expect("switch")
appendOut("switch (", false) c.appendOut("switch (", false)
parseExpression(true) c.parseExpression(true)
appendOut(") do {", true) c.appendOut(") do {", true)
expect("{") c.expect("{")
parseSwitchBlock() c.parseSwitchBlock()
expect("}") c.expect("}")
appendOut("};", true) c.appendOut("};", true)
} }
func parseSwitchBlock() { func (c *Compiler) parseSwitchBlock() {
if accept("}") { if c.accept("}") {
return return
} }
if accept("case") { if c.accept("case") {
next() c.next()
appendOut("case ", false) c.appendOut("case ", false)
parseExpression(true) c.parseExpression(true)
expect(":") c.expect(":")
appendOut(":", true) c.appendOut(":", true)
if !accept("case") && !accept("}") && !accept("default") { if !c.accept("case") && !c.accept("}") && !c.accept("default") {
appendOut("{", true) c.appendOut("{", true)
parseBlock() c.parseBlock()
appendOut("};", true) c.appendOut("};", true)
} }
} else if accept("default") { } else if c.accept("default") {
next() c.next()
expect(":") c.expect(":")
appendOut("default:", true) c.appendOut("default:", true)
if !accept("}") { if !c.accept("}") {
appendOut("{", true) c.appendOut("{", true)
parseBlock() c.parseBlock()
appendOut("};", true) c.appendOut("};", true)
} }
} }
parseSwitchBlock() c.parseSwitchBlock()
} }
func parseFor() { func (c *Compiler) parseFor() {
expect("for") c.expect("for")
appendOut("for [{", false) c.appendOut("for [{", false)
// var in first assignment is optional // var in first assignment is optional
if accept("var") { if c.accept("var") {
next() c.next()
} }
parseExpression(true) c.parseExpression(true)
expect(";") c.expect(";")
appendOut("}, {", false) c.appendOut("}, {", false)
parseExpression(true) c.parseExpression(true)
expect(";") c.expect(";")
appendOut("}, {", false) c.appendOut("}, {", false)
parseExpression(true) c.parseExpression(true)
appendOut("}] do {", true) c.appendOut("}] do {", true)
expect("{") c.expect("{")
parseBlock() c.parseBlock()
expect("}") c.expect("}")
appendOut("};", true) c.appendOut("};", true)
} }
func parseForeach() { func (c *Compiler) parseForeach() {
expect("foreach") c.expect("foreach")
expr := parseExpression(false) expr := c.parseExpression(false)
expect("{") c.expect("{")
appendOut("{", true) c.appendOut("{", true)
parseBlock() c.parseBlock()
expect("}") c.expect("}")
appendOut("} forEach ("+expr+");", true) c.appendOut("} forEach ("+expr+");", true)
} }
func parseFunction() { func (c *Compiler) parseFunction() {
expect("func") c.expect("func")
appendOut(get().token+" = {", true) c.appendOut(c.get().token+" = {", true)
next() c.next()
expect("(") c.expect("(")
parseFunctionParameter() c.parseFunctionParameter()
expect(")") c.expect(")")
expect("{") c.expect("{")
parseBlock() c.parseBlock()
expect("}") c.expect("}")
appendOut("};", true) c.appendOut("};", true)
} }
func parseFunctionParameter() { func (c *Compiler) parseFunctionParameter() {
// empty parameter list // empty parameter list
if accept("{") { if c.accept("{") {
return return
} }
appendOut("params [", false) c.appendOut("params [", false)
for !accept(")") { for !c.accept(")") {
name := get().token name := c.get().token
next() c.next()
if accept("=") { if c.accept("=") {
next() c.next()
value := get().token value := c.get().token
next() c.next()
appendOut("[\""+name+"\","+value+"]", false) c.appendOut("[\""+name+"\","+value+"]", false)
} else { } else {
appendOut("\""+name+"\"", false) c.appendOut("\""+name+"\"", false)
} }
if !accept(")") { if !c.accept(")") {
expect(",") c.expect(",")
appendOut(",", false) c.appendOut(",", false)
} }
} }
appendOut("];", true) c.appendOut("];", true)
} }
func parseReturn() { func (c *Compiler) parseReturn() {
expect("return") c.expect("return")
appendOut("return ", false) c.appendOut("return ", false)
parseExpression(true) c.parseExpression(true)
expect(";") c.expect(";")
appendOut(";", true) c.appendOut(";", true)
} }
func parseTryCatch() { func (c *Compiler) parseTryCatch() {
expect("try") c.expect("try")
expect("{") c.expect("{")
appendOut("try {", true) c.appendOut("try {", true)
parseBlock() c.parseBlock()
expect("}") c.expect("}")
expect("catch") c.expect("catch")
expect("{") c.expect("{")
appendOut("} catch {", true) c.appendOut("} catch {", true)
parseBlock() c.parseBlock()
expect("}") c.expect("}")
appendOut("};", true) c.appendOut("};", true)
} }
func parseExitWith() { func (c *Compiler) parseExitWith() {
expect("exitwith") c.expect("exitwith")
expect("{") c.expect("{")
appendOut("if (true) exitWith {", true) c.appendOut("if (true) exitWith {", true)
parseBlock() c.parseBlock()
expect("}") c.expect("}")
appendOut("};", true) c.appendOut("};", true)
} }
func parseWaitUntil() { func (c *Compiler) parseWaitUntil() {
expect("waituntil") c.expect("waituntil")
expect("(") c.expect("(")
appendOut("waitUntil {", false) c.appendOut("waitUntil {", false)
parseExpression(true) c.parseExpression(true)
if accept(";") { if c.accept(";") {
next() c.next()
appendOut(";", false) c.appendOut(";", false)
parseExpression(true) c.parseExpression(true)
} }
expect(")") c.expect(")")
expect(";") c.expect(";")
appendOut("};", true) c.appendOut("};", true)
} }
func parseInlineCode() string { func (c *Compiler) parseInlineCode() string {
expect("code") c.expect("code")
expect("(") c.expect("(")
code := get().token code := c.get().token
next() c.next()
output := "{}" output := "{}"
if len(code) > 2 { if len(code) > 2 {
output = "{"+Parse(Tokenize([]byte(code[1:len(code)-1])), pretty)+"}" //output = "{"+Parse(Tokenize([]byte(code[1:len(code)-1])), pretty)+"}"
} }
expect(")") c.expect(")")
return output return output
} }
// Everything that does not start with a keyword. // Everything that does not start with a keyword.
func parseStatement() { func (c *Compiler) parseStatement() {
// empty block // empty block
if accept("}") || accept("case") || accept("default") { if c.accept("}") || c.accept("case") || c.accept("default") {
return return
} }
// variable or function name // variable or function name
name := get().token name := c.get().token
next() c.next()
if accept("=") { if c.accept("=") {
appendOut(name, false) c.appendOut(name, false)
parseAssignment() c.parseAssignment()
} else { } else {
parseFunctionCall(true, name) c.parseFunctionCall(true, name)
expect(";") c.expect(";")
appendOut(";", true) c.appendOut(";", true)
} }
if !end() { if !c.end() {
parseBlock() c.parseBlock()
} }
} }
func parseAssignment() { func (c *Compiler) parseAssignment() {
expect("=") c.expect("=")
appendOut(" = ", false) c.appendOut(" = ", false)
parseExpression(true) c.parseExpression(true)
expect(";") c.expect(";")
appendOut(";", true) c.appendOut(";", true)
} }
func parseFunctionCall(out bool, name string) string { func (c *Compiler) parseFunctionCall(out bool, name string) string {
output := "" output := ""
expect("(") c.expect("(")
leftParams, leftParamCount := parseParameter(false) leftParams, leftParamCount := c.parseParameter(false)
expect(")") c.expect(")")
if accept("(") { if c.accept("(") {
// buildin function // buildin function
next() c.next()
rightParams, rightParamCount := parseParameter(false) rightParams, rightParamCount := c.parseParameter(false)
expect(")") c.expect(")")
if leftParamCount > 1 { if leftParamCount > 1 {
leftParams = "[" + leftParams + "]" leftParams = "[" + leftParams + "]"
@@ -369,143 +369,143 @@ func parseFunctionCall(out bool, name string) string {
} }
if out { if out {
appendOut(output, false) c.appendOut(output, false)
} }
return output return output
} }
func parseParameter(out bool) (string, int) { func (c *Compiler) parseParameter(out bool) (string, int) {
output := "" output := ""
count := 0 count := 0
for !accept(")") { for !c.accept(")") {
output += parseExpression(out) output += c.parseExpression(out)
count++ count++
if !accept(")") { if !c.accept(")") {
expect(",") c.expect(",")
output += ", " output += ", "
} }
} }
if out { if out {
appendOut(output, false) c.appendOut(output, false)
} }
return output, count return output, count
} }
func parseExpression(out bool) string { func (c *Compiler) parseExpression(out bool) string {
output := parseArith() output := c.parseArith()
for accept("<") || accept(">") || accept("&") || accept("|") || accept("=") || accept("!") { for c.accept("<") || c.accept(">") || c.accept("&") || c.accept("|") || c.accept("=") || c.accept("!") {
if accept("<") { if c.accept("<") {
output += "<" output += "<"
next() c.next()
} else if accept(">") { } else if c.accept(">") {
output += ">" output += ">"
next() c.next()
} else if accept("&") { } else if c.accept("&") {
next() c.next()
expect("&") c.expect("&")
output += "&&" output += "&&"
} else if accept("|") { } else if c.accept("|") {
next() c.next()
expect("|") c.expect("|")
output += "||" output += "||"
} else if accept("=") { } else if c.accept("=") {
output += "=" output += "="
next() c.next()
} else { } else {
next() c.next()
expect("=") c.expect("=")
output += "!=" output += "!="
} }
if accept("=") { if c.accept("=") {
output += "=" output += "="
next() c.next()
} }
output += parseExpression(false) output += c.parseExpression(false)
} }
if out { if out {
appendOut(output, false) c.appendOut(output, false)
} }
return output return output
} }
func parseIdentifier() string { func (c *Compiler) parseIdentifier() string {
output := "" output := ""
if accept("code") { if c.accept("code") {
output += parseInlineCode() output += c.parseInlineCode()
} else if seek("(") && !accept("!") && !accept("-") { } else if c.seek("(") && !c.accept("!") && !c.accept("-") {
name := get().token name := c.get().token
next() c.next()
output = "(" + parseFunctionCall(false, name) + ")" output = "(" + c.parseFunctionCall(false, name) + ")"
} else if seek("[") { } else if c.seek("[") {
output += "("+get().token output += "("+c.get().token
next() c.next()
expect("[") c.expect("[")
output += " select ("+parseExpression(false)+"))" output += " select ("+c.parseExpression(false)+"))"
expect("]") c.expect("]")
} else if accept("!") || accept("-") { } else if c.accept("!") || c.accept("-") {
output = get().token output = c.get().token
next() c.next()
output += parseTerm() output += c.parseTerm()
} else { } else {
output = get().token output = c.get().token
next() c.next()
} }
return output return output
} }
func parseTerm() string { func (c *Compiler) parseTerm() string {
if accept("(") { if c.accept("(") {
expect("(") c.expect("(")
output := "(" + parseExpression(false) + ")" output := "(" + c.parseExpression(false) + ")"
expect(")") c.expect(")")
return output return output
} }
return parseIdentifier() return c.parseIdentifier()
} }
func parseFactor() string { func (c *Compiler) parseFactor() string {
output := parseTerm() output := c.parseTerm()
for accept("*") || accept("/") { // TODO: modulo? for c.accept("*") || c.accept("/") { // TODO: modulo?
if accept("*") { if c.accept("*") {
output += "*" output += "*"
} else { } else {
output += "/" output += "/"
} }
next() c.next()
output += parseExpression(false) output += c.parseExpression(false)
} }
return output return output
} }
func parseArith() string { func (c *Compiler) parseArith() string {
output := parseFactor() output := c.parseFactor()
for accept("+") || accept("-") { for c.accept("+") || c.accept("-") {
if accept("+") { if c.accept("+") {
output += "+" output += "+"
} else { } else {
output += "-" output += "-"
} }
next() c.next()
output += parseExpression(false) output += c.parseExpression(false)
} }
return output return output

View File

@@ -1,81 +1,83 @@
package asl package asl
var tokens []Token type Compiler struct {
var tokenIndex int tokens []Token
var out string tokenIndex int
var offset int out string
var pretty bool offset int
pretty bool
}
// Initilizes the parser. // Initilizes the parser.
func initParser(token []Token, prettyPrinting bool) bool { func (c *Compiler) initParser(token []Token, prettyPrinting bool) bool {
if len(token) == 0 { if len(token) == 0 {
return false return false
} }
tokens = token c.tokens = token
tokenIndex = 0 c.tokenIndex = 0
out = "" c.out = ""
offset = 0 c.offset = 0
pretty = prettyPrinting c.pretty = prettyPrinting
return true return true
} }
// Returns true, if current token matches expected one. // Returns true, if current token matches expected one.
// Does not throw parse errors and checks if token is available. // Does not throw parse errors and checks if token is available.
func accept(token string) bool { func (c *Compiler) accept(token string) bool {
return tokenIndex < len(tokens) && tokenEqual(token, get()) return c.tokenIndex < len(c.tokens) && c.tokenEqual(token, c.get())
} }
// Hard version of "accept". // Hard version of "accept".
// Throws if current token does not match expected one. // Throws if current token does not match expected one.
func expect(token string) { func (c *Compiler) expect(token string) {
if !tokenEqual(token, get()) { if !c.tokenEqual(token, c.get()) {
panic("Parse error, expected '" + token + "' but was '" + get().token + "'") panic("Parse error, expected '" + token + "' but was '" + c.get().token + "'")
} }
next() c.next()
} }
// Returns true, if the next token matches expected one. // Returns true, if the next token matches expected one.
// Does not throw parse errors and checks if token is available. // Does not throw parse errors and checks if token is available.
func seek(token string) bool { func (c *Compiler) seek(token string) bool {
if tokenIndex+1 >= len(tokens) { if c.tokenIndex+1 >= len(c.tokens) {
return false return false
} }
return tokenEqual(token, tokens[tokenIndex+1]) return c.tokenEqual(token, c.tokens[c.tokenIndex+1])
} }
// Increases token counter, so that the next token is compared. // Increases token counter, so that the next token is compared.
func next() { func (c *Compiler) next() {
tokenIndex++ c.tokenIndex++
} }
// Returns current token or throws, if no more tokens are available. // Returns current token or throws, if no more tokens are available.
func get() Token { func (c *Compiler) get() Token {
if tokenIndex >= len(tokens) { if c.tokenIndex >= len(c.tokens) {
panic("No more tokens") panic("No more tokens")
} }
return tokens[tokenIndex] return c.tokens[c.tokenIndex]
} }
// Returns true if the end of input code was reached. // Returns true if the end of input code was reached.
func end() bool { func (c *Compiler) end() bool {
return tokenIndex == len(tokens) return c.tokenIndex == len(c.tokens)
} }
// Checks if two strings match. // Checks if two strings match.
func tokenEqual(a string, b Token) bool { func (c *Compiler) tokenEqual(a string, b Token) bool {
return a == b.token return a == b.token
} }
// Appends the output string to current SQF code output. // Appends the output string to current SQF code output.
func appendOut(str string, newLine bool) { func (c *Compiler) appendOut(str string, newLine bool) {
out += str c.out += str
if newLine && pretty { if newLine && c.pretty {
out += "\r\n" c.out += "\r\n"
} }
} }

View File

@@ -155,8 +155,9 @@ func getCompiled(t *testing.T, file string) string {
} }
tokens := asl.Tokenize(code) tokens := asl.Tokenize(code)
compiler := asl.Compiler{}
return asl.Parse(tokens, true) return compiler.Parse(tokens, true)
} }
func equal(t *testing.T, got, want string) { func equal(t *testing.T, got, want string) {

View File

@@ -101,7 +101,8 @@ func compile(path string) {
} }
token := asl.Tokenize(code) token := asl.Tokenize(code)
sqf := asl.Parse(token, pretty) compiler := asl.Compiler{}
sqf := compiler.Parse(token, pretty)
os.MkdirAll(filepath.FromSlash(path+PathSeparator+aslFiles[i].out), 0777) os.MkdirAll(filepath.FromSlash(path+PathSeparator+aslFiles[i].out), 0777)
err = ioutil.WriteFile(out, []byte(sqf), 0666) err = ioutil.WriteFile(out, []byte(sqf), 0666)