diff --git a/Script/go.mod b/Script/go.mod index 2912db5..7f323b3 100644 --- a/Script/go.mod +++ b/Script/go.mod @@ -2,4 +2,4 @@ module MeltScript go 1.21.3 -require github.com/TwiN/go-color v1.4.1 // indirect +require github.com/TwiN/go-color v1.4.1 diff --git a/Script/main.go b/Script/main.go index 21fd97d..0084707 100644 --- a/Script/main.go +++ b/Script/main.go @@ -29,23 +29,47 @@ const ( KEYWORD = "KEYWORD" // Operators - EQUALS = "EQUALS" + TEXTOPERATOR = "TEXTOPERATOR" + EQUALS = "EQUALS" + + PLUS = "PLUS" + PLUSPLUS = "PLUSPLUS" + PLUSEQUALS = "PLUSEQUALS" + + MINUS = "MINUS" + MINUSMINUS = "MINUSMINUS" + MINUSEQUALS = "MINUSEQUALS" + + TIMES = "TIMES" + DIVIDE = "DIVIDE" + MODULO = "MODULO" // OPEN_BRACE = "OPEN_BRACE" // CLOSE_BRACE = "CLOSE_BRACE" ) -func lex(source string) []token { - keywords := map[string]bool{ - "if": true, - "elseif": true, - "else": true, - "loop": true, - "for": true, - "break": true, - "continue": true, - } +var keywords = map[string]bool{ + "if": true, + "elseif": true, + "else": true, + "loop": true, + "for": true, + "break": true, + "continue": true, +} +var textOperators = map[string]bool{ + "is": true, + "and": true, + "or": true, + "not": true, + // "nand": true, + // "xor": true, + // "nor": true, + // "xnor": true, +} + +func lex(source string) []token { var tokens []token last := func(n int) token { @@ -105,7 +129,7 @@ ParseLoop: startColumn := column var stringLiteral string - + column++ i++ // skip the first quote for i < len(source) && source[i] != '"' { @@ -114,7 +138,45 @@ ParseLoop: i++ } + if i == len(source) { + fmt.Println(c.InRed("unclosed string literal")) + os.Exit(1) + } + addToken(STRING, stringLiteral, startLine, startColumn) + + case '+': + // check if it's a ++ or += or just a + + if i+1 < len(source) && source[i+1] == '+' { + addToken(PLUSPLUS, "++") + i++ + column++ + } else if i+1 < len(source) && source[i+1] == '=' { + addToken(PLUSEQUALS, "+=") + i++ + column++ + } else { + addToken(PLUS, "+") + } + case '-': + // check if it's a -- or -= or just a - + if i+1 < len(source) && source[i+1] == '-' { + addToken(MINUSMINUS, "--") + i++ + column++ + } else if i+1 < len(source) && source[i+1] == '=' { + addToken(MINUSEQUALS, "-=") + i++ + column++ + } else { + addToken(MINUS, "-") + } + case '*': + addToken(TIMES, "*") + case '/': + addToken(DIVIDE, "/") + case '%': + addToken(MODULO, "%") default: if char >= '0' && char <= '9' { startLine := line @@ -137,19 +199,14 @@ ParseLoop: // keep going until we hit a non-letter var identifierOrKeyword string - for i < len(source) && (source[i] >= 'a' && source[i] <= 'z' || source[i] >= 'A' && source[i] <= 'Z') { + for i < len(source) && + (source[i] >= 'a' && source[i] <= 'z' || + source[i] >= 'A' && source[i] <= 'Z' || + source[i] >= '0' && source[i] <= '9') { identifierOrKeyword += string(source[i]) column++ i++ } - column-- - i-- - - // check if it's a keyword - if keywords[identifierOrKeyword] { - addToken(KEYWORD, identifierOrKeyword, startLine, startColumn) - continue ParseLoop - } if i == len(source) { // you can't end a program with an identifier (yet) @@ -157,6 +214,21 @@ ParseLoop: os.Exit(1) } + column-- + i-- + + // check if it's a text operator + if textOperators[identifierOrKeyword] { + addToken(TEXTOPERATOR, identifierOrKeyword, startLine, startColumn) + continue ParseLoop + } + + // check if it's a keyword + if keywords[identifierOrKeyword] { + addToken(KEYWORD, identifierOrKeyword, startLine, startColumn) + continue ParseLoop + } + addToken(IDENTIFIER, identifierOrKeyword, startLine, startColumn) } else { fmt.Println(c.InRed("that isnt a valid character"), c.InYellow(string(char))) @@ -168,6 +240,80 @@ ParseLoop: return tokens } +func generate(tokens []token) string { + var output string + + for i := 0; i < len(tokens); i++ { + currentToken := tokens[i] + + nextToken := func(n int) token { + // gets the nth token after the current token + // skips over spaces and newlines + for i+n < len(tokens) { + if tokens[i+n].kind == SPACE || tokens[i+n].kind == NEWLINE { + n++ + } else { + return tokens[i+n] + } + } + return token{} + } + + usedIdentifiers := map[string]bool{} + + switch currentToken.kind { + case NEWLINE: + output += "\n" + case INDENT: + output += "\t" + case NUMBER: + output += currentToken.value + case STRING: + output += fmt.Sprintf("\"%s\"", currentToken.value) + case IDENTIFIER: + nextKind := nextToken(1).kind + switch nextKind { + case EQUALS: + // variable assignment + if !usedIdentifiers[currentToken.value] { + output += "local " + } + output += currentToken.value + usedIdentifiers[currentToken.value] = true + case PLUSPLUS: + output += currentToken.value + " = " + currentToken.value + " + 1" + i++ + case MINUSMINUS: + output += currentToken.value + " = " + currentToken.value + " - 1" + i++ + default: + output += currentToken.value + " " + } + case EQUALS: + output += " = " + case COMMENT: + output += " --" + currentToken.value + case TEXTOPERATOR: + switch currentToken.value { + case "is": + output += "== " + default: + output += currentToken.value + } + // case KEYWORD: + // switch currentToken.value { + // case "loop": + // output += "while true do" + // default: + // output += currentToken.value + // } + // output += " " + } + } + + return output +} + func main() { if len(os.Args) < 2 { fmt.Println(c.InRed("No target file specified!")) @@ -194,25 +340,30 @@ func main() { // replace \r\n with \n sourceString := strings.Replace(string(source), "\r\n", "\n", -1) + // remove trailing newlines + sourceString = strings.TrimRight(sourceString, "\n") tokens := lex(sourceString) - // out := generate(tokens) for _, token := range tokens { if token.kind == "SPACE" { continue } if token.kind == "NEWLINE" { - fmt.Println("──────┼─────────────┼─────────────────────────────") + fmt.Println("────────────────┼───────────────┼─────────────────────────────") continue } - toPrint := []string{ - fmt.Sprintf("%d:%d", token.line, token.column), + toPrint := []any{ + fmt.Sprintf("%s:%d:%d", target, token.line, token.column), c.InYellow(token.kind), c.InPurple(token.value), } // print in a nice format - fmt.Printf("%-5s │ %-20s │ %s\n", toPrint[0], toPrint[1], toPrint[2]) + fmt.Printf("%-15s │ %-22s │ %s\n", toPrint...) } + + out := generate(tokens) + + fmt.Println(out) } diff --git a/Script/test.melt b/Script/test.melt index cbc0ce1..6d0bdb6 100644 --- a/Script/test.melt +++ b/Script/test.melt @@ -13,3 +13,13 @@ loop print "infinite loop" print "just kidding" break + +for 5 + x++ + print "this prints 5 times" + print "x is now {x}" + +y = if x is 6 + "yes" +else + "no" diff --git a/Sync/Server/index.ts b/Sync/Server/index.ts deleted file mode 100644 index 5594165..0000000 --- a/Sync/Server/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Elysia } from "elysia" - -const app = new Elysia().get("/", () => "Hello World!").listen(2013) - -console.log(`Serving at http://${app.server?.hostname}:${app.server?.port}`)