├── .gitignore ├── Examples ├── Break statement.lfs ├── Creating variables.lfs ├── For loop.lfs ├── Binary counter.lfs ├── If statement.lfs ├── Finding factors of a number.lfs ├── Concatenating strings.lfs ├── Getting variable values.lfs ├── Getting user input.lfs └── Find primes.lfs ├── go.mod ├── supporting ├── input.go ├── dostring.go ├── print.go ├── domath.go ├── readfile.go ├── doconcat.go ├── for.go ├── if.go ├── variables.go └── lexer.go ├── go.sum ├── main.go ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.exe 3 | !PackageFile.exe -------------------------------------------------------------------------------- /Examples/Break statement.lfs: -------------------------------------------------------------------------------- 1 | for i 100 2 | print math i 3 | if math i == 15 4 | var i = 100 5 | endif 6 | endfor -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module Leafscript 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/Knetic/govaluate v3.0.0+incompatible 7 | github.com/akamensky/argparse v1.2.2 8 | ) 9 | -------------------------------------------------------------------------------- /Examples/Creating variables.lfs: -------------------------------------------------------------------------------- 1 | // creating a string variable, integer variable and performing math 2 | var name = "Simon" 3 | var number = 20 4 | var doingMath = math number*2 -------------------------------------------------------------------------------- /Examples/For loop.lfs: -------------------------------------------------------------------------------- 1 | // for loops must contain an iteration variable and a number of iterations, running from 0 to number of iterations - 1 2 | for x 20 3 | print math x 4 | endfor 5 | -------------------------------------------------------------------------------- /Examples/Binary counter.lfs: -------------------------------------------------------------------------------- 1 | for x 2 2 | for y 2 3 | for z 2 4 | for i 2 5 | print concat math x & " " & math y & " " & math z & " " & math i 6 | endfor 7 | endfor 8 | endfor 9 | endfor -------------------------------------------------------------------------------- /Examples/If statement.lfs: -------------------------------------------------------------------------------- 1 | var number = 20 2 | 3 | // use ensure that the contents of ifs and elses are indented with a tab 4 | if math number == 20 5 | print "The number is 20" 6 | else 7 | print "The number is not 20" 8 | endif -------------------------------------------------------------------------------- /Examples/Finding factors of a number.lfs: -------------------------------------------------------------------------------- 1 | var number = inputint "Enter a number to find factors of: " 2 | 3 | for x math number+1 4 | if math number%x == 0 5 | print concat math x & " is a factor of " & math number 6 | endif 7 | endfor 8 | -------------------------------------------------------------------------------- /Examples/Concatenating strings.lfs: -------------------------------------------------------------------------------- 1 | var name = "Simon" 2 | 3 | // use the keyword "concat" to perform string concatenation and "&" to join parts together 4 | var extendedName = concat string name & " is a good name" 5 | 6 | print string extendedName -------------------------------------------------------------------------------- /Examples/Getting variable values.lfs: -------------------------------------------------------------------------------- 1 | var name = "Simon" 2 | var number = 20 3 | 4 | // use the keyword "string" to get the value from a string variable and the keyword "math" to get the value from a integer/float variable 5 | print string name 6 | print math number -------------------------------------------------------------------------------- /supporting/input.go: -------------------------------------------------------------------------------- 1 | package supporting 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func input(printString string) string { 10 | scanner := bufio.NewScanner(os.Stdin) 11 | 12 | fmt.Print(printString) 13 | 14 | scanner.Scan() 15 | return scanner.Text() 16 | } 17 | -------------------------------------------------------------------------------- /Examples/Getting user input.lfs: -------------------------------------------------------------------------------- 1 | // use the keyword "input" followed by a statement to print 2 | var name = input "What is your name? " 3 | 4 | // use the keyword "inputint" to get the input as an integer/float 5 | var number = inputint "Enter a number: " 6 | 7 | print string name 8 | print math number 9 | -------------------------------------------------------------------------------- /Examples/Find primes.lfs: -------------------------------------------------------------------------------- 1 | var number = inputint "Enter a number to find primes up to: " 2 | var total = 0 3 | 4 | for x math number 5 | var y = 0 6 | for i math x 7 | if math x%i == 0 8 | var y = math y+1 9 | endif 10 | endfor 11 | 12 | if math y < 2 13 | print concat math x & " is prime" 14 | var total = math total+1 15 | endif 16 | endfor 17 | 18 | print concat math total & " primes found" -------------------------------------------------------------------------------- /supporting/dostring.go: -------------------------------------------------------------------------------- 1 | package supporting 2 | 3 | func doString(variableName string) string { 4 | // create value to return 5 | var value string 6 | 7 | // iterate through variable list 8 | for x := 0; x < len(variables); x++ { 9 | // check if names match 10 | if variables[x].Name == variableName && variables[x].Type == "str" { 11 | value = variables[x].Value 12 | break 13 | } 14 | } 15 | 16 | return value 17 | } 18 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Knetic/govaluate v1.5.0 h1:L4MyqdJSld9xr2eZcZHCWLfeIX2SBjqrwIKG1pcm/+4= 2 | github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg= 3 | github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 4 | github.com/akamensky/argparse v1.2.2 h1:P17T0ZjlUNJuWTPPJ2A5dM1wxarHgHqfYH+AZTo2xQA= 5 | github.com/akamensky/argparse v1.2.2/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA= 6 | -------------------------------------------------------------------------------- /supporting/print.go: -------------------------------------------------------------------------------- 1 | package supporting 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func customPrint(variableArray []string) { 9 | // check if printing string 10 | if strings.Contains(variableArray[1], "\"") { 11 | fmt.Println(strings.ReplaceAll(strings.Join(variableArray[1:], " "), "\"", "")) 12 | // check if doing math or printing string variable 13 | } else if variableArray[1] == "math" { 14 | fmt.Println(doMath(variableArray[2])) 15 | } else if variableArray[1] == "string" { 16 | fmt.Println(doString(variableArray[2])) 17 | } else if variableArray[1] == "concat" { 18 | fmt.Println(doConcat(strings.Join(variableArray[2:], " "))) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /supporting/domath.go: -------------------------------------------------------------------------------- 1 | package supporting 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Knetic/govaluate" 6 | "strconv" 7 | ) 8 | 9 | func doMath(expression string) string { 10 | // create dictionary to use as parameters 11 | params := make(map[string]interface{}, 8) 12 | for x := 0; x < len(variables); x++ { 13 | if variables[x].Type == "num" { 14 | params[variables[x].Name], _ = strconv.ParseFloat(variables[x].Value, 32) 15 | } 16 | } 17 | 18 | // create new expression and evaluate 19 | exp, err := govaluate.NewEvaluableExpression(expression) 20 | if err != nil { 21 | panic(err) 22 | } 23 | result, err := exp.Evaluate(params) 24 | 25 | return fmt.Sprintf("%v", result) 26 | } 27 | -------------------------------------------------------------------------------- /supporting/readfile.go: -------------------------------------------------------------------------------- 1 | package supporting 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func ParseFile(directory string) [][]string { 10 | // open project file 11 | file, err := os.Open(directory) 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | // create array of file lines 17 | lines := [][]string{} 18 | 19 | // create scanner to read file 20 | scanner := bufio.NewScanner(file) 21 | 22 | // read through file lines 23 | for scanner.Scan() { 24 | line := scanner.Text() 25 | // check if line commented out 26 | if !strings.Contains(line, "//") { 27 | //line = strings.TrimSpace(line) 28 | line = strings.TrimRight(line, "\t \n") 29 | lineSplit := strings.Split(line, " ") 30 | 31 | lines = append(lines, lineSplit) 32 | } 33 | } 34 | 35 | return lines 36 | } 37 | -------------------------------------------------------------------------------- /supporting/doconcat.go: -------------------------------------------------------------------------------- 1 | package supporting 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | func doConcat(toConcat string) string { 8 | // split inputted string into array to operate on and create final string 9 | split := strings.Split(toConcat, "&") 10 | var final string 11 | 12 | // iterate through to remove whitespace 13 | for i := 0; i < len(split); i++ { 14 | split[i] = strings.TrimSpace(split[i]) 15 | } 16 | 17 | // iterate through array and perform actions 18 | for i := 0; i < len(split); i++ { 19 | if strings.Contains(split[i], "\"") { 20 | final += strings.ReplaceAll(split[i], "\"", "") 21 | } else if strings.Contains(split[i], "string") { 22 | final += doString(strings.Split(split[i], " ")[1]) 23 | } else if strings.Contains(split[i], "math") { 24 | final += doMath(strings.Split(split[i], " ")[1]) 25 | } 26 | } 27 | 28 | return final 29 | } 30 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "Leafscript/supporting" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/akamensky/argparse" 10 | ) 11 | 12 | func main() { 13 | // create argparse 14 | parser := argparse.NewParser("Leafscript", "The interpreter of the Leafscript programming language") 15 | run := parser.String("r", "run", &argparse.Options{Help: "The path to the .lfs file to run"}) 16 | debug := parser.Flag("d", "debug", &argparse.Options{Default: false, Help: "Include to run program in debug mode"}) 17 | err := parser.Parse(os.Args) 18 | if err != nil { 19 | fmt.Println(parser.Usage(err)) 20 | } 21 | 22 | // ensure correct file format is used 23 | if strings.Contains(*run, ".lfs") { 24 | // parse file to language 25 | fileLines := supporting.ParseFile(*run) 26 | 27 | if *debug { 28 | supporting.Lex(fileLines, true) 29 | } else { 30 | supporting.Lex(fileLines, false) 31 | } 32 | } else { 33 | fmt.Println("Invalid file format. Please use the format .lfs") 34 | fmt.Println("To view usage information, include the -h flag") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 odddollar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /supporting/for.go: -------------------------------------------------------------------------------- 1 | package supporting 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | func forLoop(lines [][]string, iterVar string, iter string) { 9 | // check if iter is using variable or just number 10 | y := strings.Split(iter, " ") 11 | var iterations int 12 | if y[0] == "math" { 13 | iterations, _ = strconv.Atoi(doMath(y[1])) 14 | } else { 15 | iterations, _ = strconv.Atoi(y[0]) 16 | } 17 | 18 | // create iteration variable 19 | if !checkIfVariableExists(iterVar) { 20 | variables = append(variables, variable{ 21 | Name: iterVar, 22 | Type: "num", 23 | }) 24 | } else { 25 | variables[getVariablePosition(iterVar)].Value = "0" 26 | } 27 | 28 | // get position of iteration variable in variable array 29 | pos := getVariablePosition(iterVar) 30 | 31 | var lastValue int 32 | 33 | // main for loop 34 | for x := 0; x < iterations; x++ { 35 | // check if the iteration variable has been reset, if so break continue loop from that point 36 | // not entirely sure why this works 37 | lastValue, _ = strconv.Atoi(variables[pos].Value) 38 | if lastValue+1 != x { 39 | x = lastValue 40 | } else { 41 | variables[pos].Value = strconv.Itoa(x) 42 | } 43 | Lex(lines, globalDebug) 44 | } 45 | } 46 | 47 | func getVariablePosition(variableToCheck string) int { 48 | var pos int 49 | for x := 0; x < len(variables); x++ { 50 | if variables[x].Name == variableToCheck { 51 | pos = x 52 | break 53 | } 54 | } 55 | 56 | return pos 57 | } 58 | -------------------------------------------------------------------------------- /supporting/if.go: -------------------------------------------------------------------------------- 1 | package supporting 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Knetic/govaluate" 6 | "strings" 7 | ) 8 | 9 | func ifStatement(lines [][]string, falseLines [][]string, conditional string) { 10 | var conditionalSplitLeft, conditionalSplitRight []string 11 | var operator, final string 12 | 13 | // split conditional statement into left and right to parse to function 14 | conditionalSplit := strings.Split(conditional, " ") 15 | for x := 0; x < len(conditionalSplit); x++ { 16 | if conditionalSplit[x] == "==" || conditionalSplit[x] == "!=" || conditionalSplit[x] == "<=" || conditionalSplit[x] == ">=" || conditionalSplit[x] == "<" || conditionalSplit[x] == ">" { 17 | conditionalSplitLeft = conditionalSplit[:x] 18 | conditionalSplitRight = conditionalSplit[x+1:] 19 | operator = conditionalSplit[x] 20 | break 21 | } 22 | } 23 | 24 | // parse left and right parts of conditional to replacing function 25 | final += replaceConditionalPart(conditionalSplitLeft) 26 | final += operator 27 | final += replaceConditionalPart(conditionalSplitRight) 28 | 29 | // parse in expression for evaluation 30 | exp, err := govaluate.NewEvaluableExpression(final) 31 | if err != nil { 32 | panic(err) 33 | } 34 | result, _ := exp.Evaluate(nil) 35 | 36 | // main lexer to be run if expression is true 37 | if result == true { 38 | // parse lines into main lexer 39 | Lex(lines, globalDebug) 40 | } else { 41 | Lex(falseLines, globalDebug) 42 | } 43 | } 44 | 45 | func replaceConditionalPart(half []string) string { 46 | // check what to return 47 | if half[0] == "math" { 48 | return doMath(half[1]) 49 | } else if half[0] == "string" { 50 | return fmt.Sprintf("\"%v\"", doString(half[1])) 51 | } else if strings.Contains(half[0], "\"") { 52 | return fmt.Sprintf("\"%v\"", strings.ReplaceAll(strings.Join(half[0:], " "), "\"", "")) 53 | } else { 54 | return half[0] 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /supporting/variables.go: -------------------------------------------------------------------------------- 1 | package supporting 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | func createVar(variableArray []string) { 8 | // check if exists 9 | if !checkIfVariableExists(variableArray[1]) { 10 | variables = append(variables, variable{ 11 | Name: variableArray[1], 12 | Type: checkIfVariableIsString(variableArray[3]), 13 | }) 14 | } 15 | 16 | // loop through variables to find right one 17 | for x := 0; x < len(variables); x++ { 18 | if variables[x].Name == variableArray[1] { 19 | // check if math is being performed or input is being requested 20 | if variableArray[3] != "math" && variableArray[3] != "input" && variableArray[3] != "string" && variableArray[3] != "concat" && variableArray[3] != "inputint" { 21 | // if math not being performed, assign value to variable 22 | // remove "" if string 23 | if variables[x].Type == "num" { 24 | variables[x].Value = strings.Join(variableArray[3:], "") 25 | } else { 26 | variables[x].Value = strings.ReplaceAll(strings.Join(variableArray[3:], " "), "\"", "") 27 | } 28 | break 29 | } else if variableArray[3] == "math" { 30 | // if math is being performed, run function and assign result to variable 31 | result := doMath(variableArray[4]) 32 | variables[x].Value = result 33 | break 34 | } else if variableArray[3] == "input" { 35 | // check the string provided after "input" to print out 36 | result := input(strings.ReplaceAll(strings.Join(variableArray[4:], " "), "\"", "")) 37 | variables[x].Value = result 38 | break 39 | } else if variableArray[3] == "inputint" { 40 | // check the string provided after "input" to print out 41 | result := input(strings.ReplaceAll(strings.Join(variableArray[4:], " "), "\"", "")) 42 | variables[x].Value = result 43 | break 44 | } else if variableArray[3] == "string" { 45 | // check if another string is being assigned 46 | result := doString(variableArray[4]) 47 | variables[x].Value = result 48 | break 49 | } else if variableArray[3] == "concat" { 50 | result := doConcat(strings.Join(variableArray[4:], " ")) 51 | variables[x].Value = result 52 | break 53 | } 54 | } 55 | } 56 | } 57 | 58 | func checkIfVariableIsString(valueToCheck string) string { 59 | if strings.Contains(valueToCheck, "\"") || (valueToCheck == "input") || strings.Contains(valueToCheck, "string") || strings.Contains(valueToCheck, "concat") { 60 | return "str" 61 | } else { 62 | return "num" 63 | } 64 | } 65 | 66 | func checkIfVariableExists(variableToTest string) bool { 67 | found := false 68 | for x := 0; x < len(variables); x++ { 69 | if variables[x].Name == variableToTest { 70 | found = true 71 | break 72 | } 73 | } 74 | if found == true { 75 | return true 76 | } else { 77 | return false 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Leafscript 2 | Leafscript is a lightweight programming language created as a proof of concept by someone with no idea how to write a language. 3 | It's written entirely in Golang, and was inspired by the speed and simplicity of Lua. It can be compiled from source to a binary .exe file using the command: ```go build Leafscipt``` 4 | 5 | ## Usage 6 | Programs can be run from the command line using the Leafscript binary file. 7 | 8 | ```leafscript --run [PATH TO .lfs FILE] --debug [SET TO FALSE BY DEFAULT]``` 9 | 10 | E.g. 11 | 12 | ```leafscript --run program.lfs``` 13 | 14 | Includes a basic debugger that prints a list of all variables every line. E.g. 15 | 16 | ```leafscript --run program.lfs --debug true``` 17 | 18 | 19 | ## Features 20 | It supports: 21 | - Creating and modifying variables (strings, ints and floats) 22 | - Performing mathematical operations on numeric variables 23 | - String concatenation 24 | - For loops 25 | - If/else statements 26 | - Breaks in for loops 27 | - Nested if/for 28 | - Basic debugging mode that prints all variables every line 29 | 30 | No plans to implement in near future 31 | - Array variables 32 | - Error messages 33 | 34 | ## Examples 35 | 36 | ***Ensure tabs are used to indent and not spaces. Spaces do not work*** 37 | 38 | Additional language examples are contained within the "Examples" file 39 | 40 | #### Find all the factors of a number 41 | ``` 42 | var number = inputint "Enter a number to find factors of: " 43 | 44 | for x math number+1 45 | if math number%x == 0 46 | print concat math x & " is a factor of " & math number 47 | endif 48 | endfor 49 | ``` 50 | 51 | #### Print all binary numbers from 0 to 15 52 | ``` 53 | for x 2 54 | for y 2 55 | for z 2 56 | for i 2 57 | print concat math x & " " & math y & " " & math z & " " & math i 58 | endfor 59 | endfor 60 | endfor 61 | endfor 62 | ``` 63 | 64 | #### Print all primes up to a given number 65 | ``` 66 | var number = inputint "Enter a number to find primes up to: " 67 | var total = 0 68 | 69 | for x math number 70 | var y = 0 71 | for i math x 72 | if math x%i == 0 73 | var y = math y+1 74 | endif 75 | endfor 76 | 77 | if math y < 2 78 | print concat math x & " is prime" 79 | var total = math total+1 80 | endif 81 | endfor 82 | 83 | print concat math total & " primes found" 84 | ``` 85 | 86 | ## Changelog 87 | 88 | #### v1.0 89 | 90 | - Initial release 91 | 92 | #### v1.1 93 | 94 | - Modified command line interface to use more robust argparse library 95 | - Added ability to package .lfs files to .exe using PackageFile.exe 96 | 97 | #### v1.1.1 98 | 99 | - Fixed imports 100 | 101 | #### v1.2 102 | 103 | - Removed poor implementation of packaging files to binary 104 | -------------------------------------------------------------------------------- /supporting/lexer.go: -------------------------------------------------------------------------------- 1 | package supporting 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type variable struct { 9 | Name string 10 | Type string 11 | Value string 12 | } 13 | 14 | var variables = []variable{} 15 | var globalDebug = false 16 | 17 | func Lex(lines [][]string, debug bool) { 18 | //fmt.Println(lines) 19 | globalDebug = debug 20 | 21 | // iterate through lines on file 22 | for x := 0; x < len(lines); x++ { 23 | // run function based on keyword 24 | if strings.TrimSpace(lines[x][0]) == "var" && lines[x][2] == "=" { 25 | createVar(lines[x]) 26 | } else if strings.TrimSpace(lines[x][0]) == "print" { 27 | customPrint(lines[x]) 28 | } else if strings.TrimSpace(lines[x][0]) == "for" { 29 | // count number of tabs 30 | tabs := strings.Count(lines[x][0], "\t") 31 | 32 | forLines := [][]string{} 33 | // parse lines into for loop then skip lines in main lexer 34 | i := x + 1 35 | y := x 36 | for i < len(lines) { 37 | // break out of loop 38 | if strings.TrimSpace(lines[i][0]) == "endfor" && strings.Count(lines[i][0], "\t") == tabs { 39 | x = i 40 | break 41 | } 42 | 43 | // append current line to list of lines to parse to for loop 44 | forLines = append(forLines, lines[i]) 45 | 46 | i += 1 47 | } 48 | 49 | // parse lines to for loop lexer 50 | forLoop(forLines, lines[y][1], strings.Join(lines[y][2:], " ")) 51 | } else if strings.TrimSpace(lines[x][0]) == "if" { 52 | // count number of tabs 53 | tabs := strings.Count(lines[x][0], "\t") 54 | 55 | ifLines := [][]string{} 56 | elseLines := [][]string{} 57 | isElse := false 58 | // parse lines into if statement then skip lines in main lexer 59 | i := x + 1 60 | y := x 61 | for i < len(lines) { 62 | // break out of loop 63 | if (strings.TrimSpace(lines[i][0]) == "endif" || strings.TrimSpace(lines[i][0]) == "else") && strings.Count(lines[i][0], "\t") == tabs { 64 | // check if statement is present 65 | if strings.TrimSpace(lines[i][0]) == "else" && strings.Count(lines[i][0], "\t") == tabs { 66 | isElse = true 67 | elseLines = getElseLines(i, lines, tabs) 68 | x = i 69 | } 70 | x = i 71 | break 72 | } 73 | 74 | // append current line to list of lines to parse to if statement 75 | ifLines = append(ifLines, lines[i]) 76 | 77 | i += 1 78 | } 79 | 80 | // parse lines to if statement lexer based on whether else is present 81 | if isElse == false { 82 | ifStatement(ifLines, [][]string{}, strings.Join(lines[y][1:], " ")) 83 | } else { 84 | ifStatement(ifLines, elseLines, strings.Join(lines[y][1:], " ")) 85 | for s := x; s < len(lines); s++ { 86 | if strings.TrimSpace(lines[s][0]) == "endif" && strings.Count(lines[s][0], "\t") == tabs { 87 | x = s 88 | break 89 | } 90 | } 91 | } 92 | } 93 | 94 | // print variable list if debug mode is true 95 | if debug { 96 | fmt.Println(variables) 97 | } 98 | } 99 | } 100 | 101 | func getElseLines(currentLine int, lines [][]string, tabs int) [][]string { 102 | currentLine += 1 103 | returnLines := [][]string{} 104 | 105 | // check if reached the end of if statement 106 | for currentLine < len(lines) { 107 | if strings.TrimSpace(lines[currentLine][0]) == "endif" && strings.Count(lines[currentLine][0], "\t") == tabs { 108 | break 109 | } 110 | 111 | returnLines = append(returnLines, lines[currentLine]) 112 | 113 | currentLine += 1 114 | } 115 | 116 | return returnLines 117 | } 118 | --------------------------------------------------------------------------------