├── bsharp-lsp ├── vscode │ ├── client │ │ ├── testFixture │ │ │ ├── completion.txt │ │ │ └── diagnostics.txt │ │ ├── tsconfig.json │ │ ├── package.json │ │ ├── out │ │ │ ├── test │ │ │ │ ├── runTest.js.map │ │ │ │ ├── index.js.map │ │ │ │ ├── completion.test.js.map │ │ │ │ ├── helper.js.map │ │ │ │ ├── runTest.js │ │ │ │ ├── diagnostics.test.js.map │ │ │ │ ├── index.js │ │ │ │ ├── completion.test.js │ │ │ │ ├── helper.js │ │ │ │ └── diagnostics.test.js │ │ │ ├── extension.js.map │ │ │ └── extension.js │ │ └── src │ │ │ ├── test │ │ │ ├── runTest.ts │ │ │ ├── index.ts │ │ │ ├── completion.test.ts │ │ │ ├── helper.ts │ │ │ └── diagnostics.test.ts │ │ │ └── extension.ts │ ├── README.md │ ├── tsconfig.json │ ├── language-configuration.json │ ├── LICENSE │ ├── package.json │ └── language-highlight.json ├── README.md ├── config.go ├── build.go ├── sync.go ├── ir.go ├── diagnostics.go ├── main.go └── goto.go ├── old ├── README.md └── ssa │ ├── phi.go │ ├── bspgen │ └── stmts.go │ ├── ssagen │ ├── val.go │ ├── nodes.go │ ├── consts.go │ ├── fn.go │ ├── var.go │ ├── ssagen.go │ ├── ssagen_test.go │ └── block.go │ ├── constrm │ ├── const.go │ ├── phirm.go │ ├── cast.go │ ├── constrm.go │ └── eval.go │ ├── pipeline │ └── main.go │ ├── memrm │ └── memrm.go │ ├── consts.go │ ├── fn.go │ ├── val.go │ ├── ir.go │ └── var.go ├── bpp ├── funcs │ ├── blds.bsp │ ├── blks.bsp │ ├── vars.bsp │ ├── math.bsp │ └── funcs.bsp ├── run.sh ├── main.bsp └── interp.bsp ├── bsp ├── run.sh ├── bsp.sh ├── size.sh ├── build.sh ├── ir │ ├── basic.bsp │ ├── build.bsp │ ├── ops.bsp │ ├── scope.bsp │ └── ir.bsp └── main.bsp ├── std ├── errors.bsp ├── std.go ├── math.bsp ├── json.bsp └── strings.bsp ├── backends ├── interpreter │ ├── var.go │ ├── stack.go │ ├── extensions.go │ ├── fns.go │ ├── interpreter.go │ ├── basic.go │ └── block.go ├── cgen │ ├── scope.go │ ├── var.go │ ├── struct.go │ ├── fns.go │ ├── ops.go │ ├── any.go │ ├── types.go │ └── stmts.go ├── bstar │ ├── stmts.go │ ├── fns.go │ ├── bstar.go │ └── block.go └── bsp │ ├── bsp.go │ ├── stmts.go │ └── nodes.go ├── examples ├── bot │ ├── uses.bsp │ └── wordle.bsp ├── import.bsp └── main.bsp ├── parser ├── parser.go ├── parse.go └── nodes.go ├── .gitignore ├── bot ├── test │ └── main.go ├── bot.go ├── db │ ├── autocomplete.go │ ├── save.go │ ├── read.go │ └── db.go ├── lb.go └── editcmd.go ├── fs.go ├── ir ├── build.go ├── extensions.go ├── import.go ├── const.go ├── util.go ├── vars.go ├── ir.go └── scope.go ├── README.md ├── go.mod ├── LICENSE ├── tokens ├── tokenizer.go └── stream.go ├── bsharp_test.go └── types └── tokenize.go /bsharp-lsp/vscode/client/testFixture/completion.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /old/README.md: -------------------------------------------------------------------------------- 1 | This folder contains failed B# projects. -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/testFixture/diagnostics.txt: -------------------------------------------------------------------------------- 1 | ANY browsers, ANY OS. -------------------------------------------------------------------------------- /bsharp-lsp/vscode/README.md: -------------------------------------------------------------------------------- 1 | # B# Extension 2 | The B# programming language! -------------------------------------------------------------------------------- /bpp/funcs/blds.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "bpp/funcs/vars.bsp"] 2 | [IMPORT "bpp/funcs/math.bsp"] 3 | [IMPORT "bpp/funcs/ops.bsp"] 4 | [IMPORT "bpp/funcs/blks.bsp"] -------------------------------------------------------------------------------- /bsp/run.sh: -------------------------------------------------------------------------------- 1 | bsharp run bsp/main.bsp bsp/parser.bsp bsp/tokens.bsp bsp/types.bsp bsp/ir/ir.bsp bsp/ir/scope.bsp bsp/ir/build.bsp bsp/ir/stmts.bsp bsp/ir/basic.bsp bsp/ir/ops.bsp -------------------------------------------------------------------------------- /bpp/run.sh: -------------------------------------------------------------------------------- 1 | bsharp run bpp/main.bsp bpp/parser.bsp bpp/interp.bsp bpp/funcs/vars.bsp bpp/funcs/funcs.bsp bpp/funcs/math.bsp bpp/funcs/ops.bsp bpp/funcs/blks.bsp bpp/funcs/blds.bsp -t -------------------------------------------------------------------------------- /bsp/bsp.sh: -------------------------------------------------------------------------------- 1 | bsharp ir bsp/main.bsp bsp/parser.bsp bsp/tokens.bsp bsp/types.bsp bsp/ir/ir.bsp bsp/ir/scope.bsp bsp/ir/build.bsp bsp/ir/stmts.bsp bsp/ir/basic.bsp bsp/ir/ops.bsp -o main.bsp -------------------------------------------------------------------------------- /bsp/size.sh: -------------------------------------------------------------------------------- 1 | # Analyze size of binary using bloaty 2 | clang -g examples/a.c -o a.out 3 | bloaty a.out -d symbols --debug-file=a.out.dSYM/Contents/Resources/DWARF/a.out -n 10000 --domain=file > a.txt 4 | rm -rf a.out.dSYM -------------------------------------------------------------------------------- /bsp/build.sh: -------------------------------------------------------------------------------- 1 | bsharp build bsp/main.bsp bsp/parser.bsp bsp/tokens.bsp bsp/types.bsp bsp/ir/ir.bsp bsp/ir/scope.bsp bsp/ir/build.bsp bsp/ir/stmts.bsp bsp/ir/basic.bsp bsp/ir/ops.bsp -o examples/a.c 2 | clang examples/a.c -o a.out 3 | ./a.out -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2020", 5 | "lib": ["es2020"], 6 | "outDir": "out", 7 | "rootDir": "src", 8 | "sourceMap": true 9 | }, 10 | "include": ["src"], 11 | "exclude": ["node_modules", ".vscode-test"] 12 | } 13 | -------------------------------------------------------------------------------- /bsharp-lsp/README.md: -------------------------------------------------------------------------------- 1 | # B# LSP 2 | The B# Language Server! 3 | 4 | ## Installing 5 | To install on VSCode, do the following: 6 | 1. Install the extension (`bsharp`) 7 | 2. Install the Language Server using: 8 | ```sh 9 | go install github.com/Nv7-Github/bsharp/bsharp-lsp@latest 10 | ``` 11 | **NOTE**: Re-run this command to get a newer version! -------------------------------------------------------------------------------- /std/errors.bsp: -------------------------------------------------------------------------------- 1 | # This lib may change as the language evolves 2 | [TYPEDEF option ANY] 3 | [TYPEDEF error STRUCT{msg:STRING}] 4 | 5 | [FUNC ERROR [PARAM msg STRING] [RETURNS option] 6 | [DEFINE out [MAKE error]] 7 | [SET [VAR out] msg [VAR msg]] 8 | [RETURN [ANY [VAR out]]] 9 | ] 10 | 11 | [FUNC FAIL [PARAM err ANY] 12 | [PANIC [GET [CAST [VAR err] error] msg]] 13 | ] -------------------------------------------------------------------------------- /bsharp-lsp/vscode/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2020", 5 | "lib": ["es2020"], 6 | "outDir": "out", 7 | "rootDir": "src", 8 | "sourceMap": true 9 | }, 10 | "include": [ 11 | "src" 12 | ], 13 | "exclude": [ 14 | "node_modules", 15 | ".vscode-test" 16 | ], 17 | "references": [ 18 | { "path": "./client" } 19 | ] 20 | } -------------------------------------------------------------------------------- /bsp/ir/basic.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "bsp/ir/ir.bsp"] 2 | 3 | [FUNC addPrint [PARAM b builder] [PARAM pos pos] [PARAM args ARRAY{node}] [RETURNS ANY] 4 | [DEFINE out [MAKE node]] 5 | [SET [VAR out] typ [_NULL]] 6 | [SET [VAR out] kind [CONST nodeKindPrint]] 7 | [SET [VAR out] val [ANY [INDEX [VAR args] 0]]] # Arg 8 | [SET [VAR out] pos [VAR pos]] 9 | [RETURN [ANY [VAR out]]] 10 | ] 11 | 12 | [addNodeBuilder "PRINT" [ARRAY [_STRING]] [FN addPrint]] -------------------------------------------------------------------------------- /backends/interpreter/var.go: -------------------------------------------------------------------------------- 1 | package interpreter 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | "github.com/Nv7-Github/bsharp/types" 6 | ) 7 | 8 | func (i *Interpreter) evalVarNode(n *ir.VarNode) (*Value, error) { 9 | return i.stack.Get(n.ID), nil 10 | } 11 | 12 | func (i *Interpreter) evalDefineNode(n *ir.DefineNode) (*Value, error) { 13 | v, err := i.evalNode(n.Value) 14 | if err != nil { 15 | return nil, err 16 | } 17 | i.stack.Set(n.Var, v, n.InScope) 18 | return NewValue(types.NULL, nil), nil 19 | } 20 | -------------------------------------------------------------------------------- /std/std.go: -------------------------------------------------------------------------------- 1 | package std 2 | 3 | import ( 4 | "embed" 5 | "io" 6 | ) 7 | 8 | //go:embed *.bsp 9 | var fs embed.FS 10 | 11 | var Std = make(map[string]string) 12 | 13 | func init() { 14 | dirs, err := fs.ReadDir(".") 15 | if err != nil { 16 | panic(err) 17 | } 18 | for _, val := range dirs { 19 | f, err := fs.Open(val.Name()) 20 | if err != nil { 21 | panic(err) 22 | } 23 | v, err := io.ReadAll(f) 24 | if err != nil { 25 | panic(err) 26 | } 27 | Std[val.Name()] = string(v) 28 | f.Close() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/bot/uses.bsp: -------------------------------------------------------------------------------- 1 | # DB Schema: MAP{USERID,USES} (Note: Uses stored as string) 2 | 3 | [DEFINE uses 0] 4 | [IF [COMPARE [DB EXISTS [USERID]] == "true"] # Note: [DB EXISTS] returns "true" if true, "false" if false 5 | [DEFINE uses [INT [DB GET [USERID]]]] 6 | ] 7 | 8 | [DEFINE uses [MATH [VAR uses] + 1]] 9 | [DEFINE s ""] 10 | [IF [COMPARE [VAR uses] == 1] 11 | [DEFINE s "s"] 12 | ] 13 | [PRINT [CONCAT "You have used this program " [STRING [VAR uses]] " time" [VAR s] "."]] 14 | 15 | # Save 16 | [DB SET [USERID] [STRING [VAR uses]]] -------------------------------------------------------------------------------- /bsharp-lsp/config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/tliron/glsp" 5 | protocol "github.com/tliron/glsp/protocol_3_16" 6 | ) 7 | 8 | type Config struct { 9 | DiscordSupport bool 10 | } 11 | 12 | func config(context *glsp.Context, doc string) *Config { 13 | var out []bool 14 | context.Call(protocol.ServerWorkspaceConfiguration, &protocol.ConfigurationParams{ 15 | Items: []protocol.ConfigurationItem{ 16 | { 17 | ScopeURI: &doc, 18 | Section: Ptr("discordSupport"), 19 | }, 20 | }, 21 | }, &out) 22 | return &Config{ 23 | DiscordSupport: out[0], 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lsp-sample-client", 3 | "description": "VSCode part of a language server", 4 | "author": "Microsoft Corporation", 5 | "license": "MIT", 6 | "version": "0.0.1", 7 | "publisher": "vscode", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/Microsoft/vscode-extension-samples" 11 | }, 12 | "engines": { 13 | "vscode": "^1.63.0" 14 | }, 15 | "dependencies": { 16 | "vscode-languageclient": "^7.0.0" 17 | }, 18 | "devDependencies": { 19 | "@types/vscode": "^1.63.0", 20 | "@vscode/test-electron": "^2.1.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import "github.com/Nv7-Github/bsharp/tokens" 4 | 5 | type Parser struct { 6 | t *tokens.Tokenizer 7 | Nodes []Node 8 | } 9 | 10 | func NewParser(t *tokens.Tokenizer) *Parser { 11 | return &Parser{ 12 | t: t, 13 | Nodes: make([]Node, 0), 14 | } 15 | } 16 | 17 | func (p *Parser) Parse() error { 18 | for p.t.HasNext() { 19 | n, err := p.ParseNode() 20 | if err != nil { 21 | return err 22 | } 23 | p.Nodes = append(p.Nodes, n) 24 | } 25 | return nil 26 | } 27 | 28 | func (p *Parser) Filename() string { 29 | return p.t.Filename() 30 | } 31 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/out/test/runTest.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"runTest.js","sourceRoot":"","sources":["../../src/test/runTest.ts"],"names":[],"mappings":";;AAAA;;;gGAGgG;AAChG,6BAA6B;AAE7B,yDAAiD;AAEjD,KAAK,UAAU,IAAI;IAClB,IAAI;QACH,4DAA4D;QAC5D,yCAAyC;QACzC,MAAM,wBAAwB,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QAEtE,0BAA0B;QAC1B,iCAAiC;QACjC,MAAM,kBAAkB,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAE9D,0DAA0D;QAC1D,MAAM,IAAA,wBAAQ,EAAC,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,CAAC,CAAC;KACjE;IAAC,OAAO,GAAG,EAAE;QACb,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;KAChB;AACF,CAAC;AAED,IAAI,EAAE,CAAC"} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | # Data 18 | bot/test/data/* 19 | a.c 20 | testdata/* 21 | .vscode/* 22 | a.txt 23 | a.bsp 24 | 25 | # Secret! 26 | bot/test/token.txt 27 | 28 | # Compiled LSP 29 | bsharp-lsp/lsp 30 | bsharp-lsp/vscode/node_modules 31 | bsharp-lsp/vscode/client/node_modules 32 | bsharp-lsp/vscode/*.vsix -------------------------------------------------------------------------------- /bpp/funcs/blks.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "bpp/interp.bsp"] 2 | [IMPORT "bpp/funcs/funcs.bsp"] 3 | [IMPORT "math.bsp"] 4 | 5 | [FUNC node_if [PARAM self node] [PARAM args ARRAY{node}] [RETURNS ANY] 6 | # Eval cond 7 | [DEFINE cond_err [evalNode [INDEX [VAR args] 1]]] # first arg is function name 8 | [IF [CANCAST [VAR cond_err] error] 9 | [RETURN [VAR cond_err]] 10 | ] 11 | 12 | [DEFINE cond [CAST [GET [CAST [VAR cond_err] node] val] INT]] 13 | 14 | [IF [COMPARE [VAR cond] == 1] # True 15 | [RETURN [evalNode [INDEX [VAR args] 2]]] 16 | ] 17 | 18 | [RETURN [evalNode [INDEX [VAR args] 3]]] 19 | ] 20 | 21 | [ADD_BLK "IF" [FN node_if]] 22 | -------------------------------------------------------------------------------- /bpp/funcs/vars.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "bpp/interp.bsp"] 2 | [IMPORT "bpp/funcs/funcs.bsp"] 3 | 4 | [DEFINE vars [MAKE MAP{STRING, node}]] 5 | 6 | [FUNC node_define [PARAM self node] [PARAM args ARRAY{node}] [RETURNS ANY] 7 | [DEFINE name [CAST [GET [INDEX [VAR args] 0] val] STRING]] 8 | [SET [VAR vars] [VAR name] [INDEX [VAR args] 1]] 9 | [RETURN [ANY [EMPTY_NODE]]] 10 | ] 11 | 12 | [ADD_BLD "DEFINE" [FN node_define]] 13 | 14 | [FUNC node_var [PARAM self node] [PARAM args ARRAY{node}] [RETURNS ANY] 15 | [DEFINE name [CAST [GET [INDEX [VAR args] 0] val] STRING]] 16 | [RETURN [ANY [GET [VAR vars] [VAR name]]]] 17 | ] 18 | 19 | [ADD_BLD "VAR" [FN node_var]] -------------------------------------------------------------------------------- /bot/test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | "os" 7 | "os/signal" 8 | "time" 9 | 10 | "github.com/Nv7-Github/bsharp/bot" 11 | ) 12 | 13 | //go:embed token.txt 14 | var token string 15 | 16 | const guild = "903380812135825459" 17 | 18 | func main() { 19 | start := time.Now() 20 | fmt.Println("Loading Bot...") 21 | bot, err := bot.NewBot("data", token) 22 | if err != nil { 23 | panic(err) 24 | } 25 | fmt.Println("Loaded bot in", time.Since(start)) 26 | 27 | stop := make(chan os.Signal, 1) 28 | signal.Notify(stop, os.Interrupt) 29 | fmt.Println("Press Ctrl+C to exit!") 30 | <-stop 31 | 32 | bot.Close() 33 | } 34 | -------------------------------------------------------------------------------- /examples/import.bsp: -------------------------------------------------------------------------------- 1 | [FUNC ADD [PARAM a INT] [PARAM b INT] [RETURNS INT] 2 | [RETURN [MATH [VAR a] + [VAR b]]] 3 | ] 4 | 5 | [FUNC SUB [PARAM a INT] [PARAM b INT] [RETURNS INT] 6 | [RETURN [MATH [VAR a] - [VAR b]]] 7 | ] 8 | 9 | [FUNC MUL [PARAM a INT] [PARAM b INT] [RETURNS INT] 10 | [RETURN [MATH [VAR a] * [VAR b]]] 11 | ] 12 | 13 | [FUNC DIV [PARAM a INT] [PARAM b INT] [RETURNS INT] 14 | [RETURN [MATH [VAR a] / [VAR b]]] 15 | ] 16 | 17 | [FUNC MOD [PARAM a INT] [PARAM b INT] [RETURNS INT] 18 | [RETURN [MATH [VAR a] % [VAR b]]] 19 | ] 20 | 21 | [FUNC POW [PARAM a INT] [PARAM b INT] [RETURNS INT] 22 | [RETURN [MATH [VAR a] ^ [VAR b]]] 23 | ] 24 | 25 | -------------------------------------------------------------------------------- /old/ssa/phi.go: -------------------------------------------------------------------------------- 1 | package ssa 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/Nv7-Github/bsharp/types" 7 | ) 8 | 9 | type Phi struct { 10 | Values []ID 11 | Typ types.Type 12 | Variable int // Annotation for putting back mem 13 | } 14 | 15 | func (p *Phi) Type() types.Type { return p.Typ } 16 | func (p *Phi) String() string { 17 | out := &strings.Builder{} 18 | out.WriteString("φ(") 19 | for i, val := range p.Values { 20 | out.WriteString(val.String()) 21 | if i != len(p.Values)-1 { 22 | out.WriteString(", ") 23 | } 24 | } 25 | out.WriteRune(')') 26 | return out.String() 27 | } 28 | func (p *Phi) Args() []ID { return p.Values } 29 | func (p *Phi) SetArgs(v []ID) { p.Values = v } 30 | -------------------------------------------------------------------------------- /fs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/Nv7-Github/bsharp/parser" 8 | "github.com/Nv7-Github/bsharp/tokens" 9 | ) 10 | 11 | type dirFS struct { 12 | files map[string]struct{} 13 | } 14 | 15 | func (d *dirFS) Parse(name string) (*parser.Parser, error) { 16 | if _, ok := d.files[name]; !ok { 17 | return nil, fmt.Errorf("bsharp: file not found: %s", name) 18 | } 19 | src, err := os.ReadFile(name) 20 | if err != nil { 21 | return nil, err 22 | } 23 | stream := tokens.NewTokenizer(tokens.NewStream(name, string(src))) 24 | err = stream.Tokenize() 25 | if err != nil { 26 | return nil, err 27 | } 28 | parser := parser.NewParser(stream) 29 | 30 | err = parser.Parse() 31 | return parser, err 32 | } 33 | -------------------------------------------------------------------------------- /old/ssa/bspgen/stmts.go: -------------------------------------------------------------------------------- 1 | package bspgen 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Nv7-Github/bsharp/ir" 7 | "github.com/Nv7-Github/bsharp/old/ssa" 8 | ) 9 | 10 | func (b *BSPGen) Add(id ssa.ID) ir.Node { 11 | info := b.Instructions[id] 12 | instr := b.Blocks[info.Block].Instructions[id] 13 | switch i := instr.(type) { 14 | case *ssa.LiveIRValue: 15 | switch i.Kind { 16 | case ssa.IRNodePrint: 17 | return ir.NewCallNode(&ir.PrintNode{Arg: b.Add(i.Params[0])}, info.Pos) 18 | 19 | default: 20 | panic(fmt.Errorf("unknown live ir kind: %s", i.Kind.String())) 21 | } 22 | 23 | case *ssa.Const: 24 | return ir.NewConst(i.Typ, info.Pos, i.Value) 25 | 26 | default: 27 | panic(fmt.Errorf("unknown instruction type: %T", i)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "#" 4 | }, 5 | "brackets": [ 6 | ["[", "]"] 7 | ], 8 | "autoClosingPairs": [ 9 | { 10 | "open": "{", 11 | "close": "}", 12 | "notIn": ["string", "comment"] 13 | }, 14 | { 15 | "open": "[", 16 | "close": "]", 17 | "notIn": ["string", "comment"] 18 | }, 19 | { 20 | "open": "\"", 21 | "close": "\"", 22 | "notIn": ["string"] 23 | }, 24 | { 25 | "open": "'", 26 | "close": "'", 27 | "notIn": ["string"], 28 | } 29 | ], 30 | "surroundingPairs": [ 31 | ["[", "]"], 32 | ["\"", "\""] 33 | ], 34 | "folding": { 35 | "markers": { 36 | "start": "^(?:\\[IF|\\[WHILE|\\[SWITCH|\\[CASE|\\[FUNC).+", 37 | "end": "^]" 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /ir/build.go: -------------------------------------------------------------------------------- 1 | package ir 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Nv7-Github/bsharp/parser" 7 | ) 8 | 9 | func (b *Builder) Build(p *parser.Parser, fs FS) error { 10 | b.imported[p.Filename()] = empty{} // Mark as imported 11 | 12 | err := b.importPass(p, fs) 13 | if err != nil { 14 | return err 15 | } 16 | err = b.defPass(p) 17 | if err != nil { 18 | return err 19 | } 20 | err = b.functionPass(p) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | // Build nodes 26 | for _, node := range p.Nodes { 27 | node, err := b.buildNode(node) 28 | if err != nil { 29 | return err 30 | } 31 | if node != nil { 32 | b.Body = append(b.Body, node) 33 | } 34 | } 35 | 36 | if len(b.Errors) > 0 { 37 | return fmt.Errorf("built with %d errors", len(b.Errors)) 38 | } 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /old/ssa/ssagen/val.go: -------------------------------------------------------------------------------- 1 | package ssagen 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | "github.com/Nv7-Github/bsharp/old/ssa" 6 | "github.com/Nv7-Github/bsharp/tokens" 7 | ) 8 | 9 | func (s *SSAGen) addConst(n *ir.Const) ssa.ID { 10 | return s.blk.AddInstruction(&ssa.Const{ 11 | Value: n.Value, 12 | Typ: n.Type(), 13 | }, n.Pos()) 14 | } 15 | 16 | func (s *SSAGen) addCompare(pos *tokens.Pos, n *ir.CompareNode) ssa.ID { 17 | return s.blk.AddInstruction(&ssa.Compare{ 18 | Op: n.Op, 19 | Typ: n.Type(), 20 | Lhs: s.Add(n.Lhs), 21 | Rhs: s.Add(n.Rhs), 22 | }, pos) 23 | } 24 | 25 | func (s *SSAGen) addMath(pos *tokens.Pos, n *ir.MathNode) ssa.ID { 26 | return s.blk.AddInstruction(&ssa.Math{ 27 | Op: n.Op, 28 | Typ: n.Type(), 29 | Lhs: s.Add(n.Lhs), 30 | Rhs: s.Add(n.Rhs), 31 | }, pos) 32 | } 33 | -------------------------------------------------------------------------------- /old/ssa/ssagen/nodes.go: -------------------------------------------------------------------------------- 1 | package ssagen 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | "github.com/Nv7-Github/bsharp/old/ssa" 6 | ) 7 | 8 | func (s *SSAGen) newIRNode(kind ssa.IRNode, n *ir.CallNode) ssa.ID { 9 | a := n.Call.Args() 10 | pars := make([]ssa.ID, len(a)) 11 | for i, par := range a { 12 | pars[i] = s.Add(par) 13 | } 14 | return s.blk.AddInstruction(&ssa.IRValue{ 15 | Kind: kind, 16 | Params: pars, 17 | Typ: n.Type(), 18 | }, n.Pos()) 19 | } 20 | 21 | func (s *SSAGen) newLiveIRNode(kind ssa.LiveIRNode, n *ir.CallNode) ssa.ID { 22 | a := n.Call.Args() 23 | pars := make([]ssa.ID, len(a)) 24 | for i, par := range a { 25 | pars[i] = s.Add(par) 26 | } 27 | return s.blk.AddInstruction(&ssa.LiveIRValue{ 28 | Kind: kind, 29 | Params: pars, 30 | Typ: n.Type(), 31 | }, n.Pos()) 32 | } 33 | -------------------------------------------------------------------------------- /old/ssa/ssagen/consts.go: -------------------------------------------------------------------------------- 1 | package ssagen 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | "github.com/Nv7-Github/bsharp/old/ssa" 6 | "github.com/Nv7-Github/bsharp/tokens" 7 | ) 8 | 9 | func (s *SSAGen) addConcat(pos *tokens.Pos, n *ir.ConcatNode) ssa.ID { 10 | vals := make([]ssa.ID, len(n.Values)) 11 | for i, val := range n.Values { 12 | vals[i] = s.Add(val) 13 | } 14 | return s.blk.AddInstruction(&ssa.Concat{ 15 | Values: vals, 16 | }, pos) 17 | } 18 | 19 | func (s *SSAGen) addLogicalOp(pos *tokens.Pos, n *ir.LogicalOpNode) ssa.ID { 20 | lhs := s.Add(n.Val) 21 | if n.Rhs == nil { 22 | return s.blk.AddInstruction(&ssa.LogicalOp{ 23 | Op: n.Op, 24 | Lhs: lhs, 25 | }, pos) 26 | } 27 | rhs := s.Add(n.Rhs) 28 | return s.blk.AddInstruction(&ssa.LogicalOp{ 29 | Op: n.Op, 30 | Lhs: lhs, 31 | Rhs: &rhs, 32 | }, pos) 33 | } 34 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/out/extension.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":";AAAA;;;gGAGgG;;;AAIhG,oCAAqC;AAErC,qDAKoC;AAEpC,IAAI,MAAsB,CAAC;AAEpB,KAAK,UAAU,QAAQ,CAAC,OAAyB;IACvD,oFAAoF;IACpF,qCAAqC;IAErC,aAAa;IACb,IAAI,MAAM,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,QAAQ,EAAE,CAAC;IACrD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpB,MAAM,aAAa,GAAkB;QACpC,OAAO,EAAG,MAAM,CAAC,IAAI,EAAE,GAAG,iBAAiB;QACzC,IAAI,EAAE,EAAE;QACR,SAAS,EAAE,oBAAa,CAAC,KAAK,EAAE,gCAAgC;KAClE,CAAC;IAEF,yCAAyC;IACzC,MAAM,aAAa,GAA0B;QAC5C,+CAA+C;QAC/C,gBAAgB,EAAE,CAAC,EAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAC,CAAC;KAC1D,CAAC;IAEF,mDAAmD;IACnD,MAAM,GAAG,IAAI,qBAAc,CAC1B,QAAQ,EACR,oBAAoB,EACpB,aAAa,EACb,aAAa,CACb,CAAC;IAGF,qDAAqD;IACrD,MAAM,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC;AA9BD,4BA8BC;AAED,SAAgB,UAAU;IACzB,IAAI,CAAC,MAAM,EAAE;QACZ,OAAO,SAAS,CAAC;KACjB;IACD,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC;AALD,gCAKC"} -------------------------------------------------------------------------------- /old/ssa/ssagen/fn.go: -------------------------------------------------------------------------------- 1 | package ssagen 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | "github.com/Nv7-Github/bsharp/old/ssa" 6 | ) 7 | 8 | func (s *SSAGen) addFnCall(n *ir.FnCallNode) ssa.ID { 9 | args := make([]ssa.ID, len(n.Params)) 10 | for i, arg := range n.Params { 11 | args[i] = s.Add(arg) 12 | } 13 | return s.blk.AddInstruction(&ssa.FnCall{ 14 | Fn: s.Add(n.Fn), 15 | Params: args, 16 | Typ: n.Type(), 17 | }, n.Pos()) 18 | } 19 | 20 | func (s *SSAGen) addReturn(n *ir.ReturnNode) ssa.ID { 21 | s.blk.EndInstructionReturn(s.Add(n.Value)) 22 | s.blk = nil 23 | return ssa.NullID() 24 | } 25 | 26 | func (s *SSAGen) addExtensionCall(n *ir.ExtensionCall) ssa.ID { 27 | args := make([]ssa.ID, len(n.Args)) 28 | for i, arg := range n.Args { 29 | args[i] = s.Add(arg) 30 | } 31 | return s.blk.AddInstruction(&ssa.ExtensionCall{ 32 | Fn: n.Name, 33 | Params: args, 34 | Typ: n.Type(), 35 | }, n.Pos()) 36 | } 37 | -------------------------------------------------------------------------------- /backends/interpreter/stack.go: -------------------------------------------------------------------------------- 1 | package interpreter 2 | 3 | type scope struct { 4 | vals map[int]*Value 5 | } 6 | 7 | type stack struct { 8 | vals []scope 9 | } 10 | 11 | func (s *stack) Push() { 12 | s.vals = append(s.vals, scope{ 13 | vals: make(map[int]*Value), 14 | }) 15 | } 16 | 17 | func (s *stack) Pop() { 18 | s.vals = s.vals[:len(s.vals)-1] 19 | } 20 | 21 | func (s *stack) Set(id int, val *Value, redefine bool) { 22 | if redefine { 23 | s.vals[len(s.vals)-1].vals[id] = val 24 | } else { 25 | for i := len(s.vals) - 1; i >= 0; i-- { 26 | if _, exists := s.vals[i].vals[id]; exists { 27 | s.vals[i].vals[id] = val 28 | return 29 | } 30 | } 31 | 32 | // Not found, put in current scope 33 | s.vals[len(s.vals)-1].vals[id] = val 34 | } 35 | } 36 | 37 | func (s *stack) Get(id int) *Value { 38 | for i := len(s.vals) - 1; i >= 0; i-- { 39 | if val, exists := s.vals[i].vals[id]; exists { 40 | return val 41 | } 42 | } 43 | 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /old/ssa/constrm/const.go: -------------------------------------------------------------------------------- 1 | package constrm 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/Nv7-Github/bsharp/ir" 7 | "github.com/Nv7-Github/bsharp/old/ssa" 8 | "github.com/Nv7-Github/bsharp/types" 9 | ) 10 | 11 | func evalConcat(blk *ssa.Block, i *ssa.Concat) *ssa.Const { 12 | out := &strings.Builder{} 13 | for _, par := range i.Values { 14 | out.WriteString(cnst(blk, par).(string)) 15 | } 16 | return &ssa.Const{Typ: types.STRING, Value: out.String()} 17 | } 18 | 19 | func evalLogicalOp(blk *ssa.Block, i *ssa.LogicalOp) *ssa.Const { 20 | switch i.Op { 21 | case ir.LogicalOpAnd: 22 | return &ssa.Const{Typ: types.BOOL, Value: cnst(blk, i.Lhs).(bool) && cnst(blk, *i.Rhs).(bool)} 23 | 24 | case ir.LogicalOpOr: 25 | return &ssa.Const{Typ: types.BOOL, Value: cnst(blk, i.Lhs).(bool) || cnst(blk, *i.Rhs).(bool)} 26 | 27 | case ir.LogicalOpNot: 28 | return &ssa.Const{Typ: types.BOOL, Value: !cnst(blk, i.Lhs).(bool)} 29 | 30 | default: 31 | return nil 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # B# 2 | Welcome to the B# programming language! Check out the [docs](docs/docs.md) to learn how to code in B#, and keep on reading to learn how to run it on your system! 3 | 4 | ## Installation 5 | To install or update B#, run 6 | ```sh 7 | go install github.com/b-Development-Team/bsharp@latest 8 | ``` 9 | 10 | ## Usage 11 | To run a file or multiple files, do 12 | ```sh 13 | bsharp run file.bsp 14 | ``` 15 | 16 | To time how long it takes to run a B# program, use the `--time` or `-t` flag. 17 | ``` 18 | bsharp run file.bsp -t 19 | ``` 20 | 21 | To compile a file, do 22 | ``` 23 | bsharp build file.bsp -o file 24 | ``` 25 | This will produce a compiled executable. 26 | 27 | The C source code that is generated can be viewed using 28 | ``` 29 | bsharp build file.bsp -o file.c 30 | ``` 31 | 32 | To time how long it takes to compile a B# program, use the `--time` or `-t` flag. 33 | ``` 34 | bsharp build file.bsp -o file -t 35 | ``` -------------------------------------------------------------------------------- /bsp/ir/build.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "bsp/ir/ir.bsp"] 2 | [IMPORT "bsp/parser.bsp"] 3 | [IMPORT "bsp/ir/stmts.bsp"] 4 | [IMPORT "errors.bsp"] 5 | 6 | # Builders 7 | [IMPORT "bsp/ir/basic.bsp"] 8 | [IMPORT "bsp/ir/ops.bsp"] 9 | 10 | [TYPEDEF ir STRUCT{body:ARRAY{node}}] 11 | 12 | [FUNC irBuild [PARAM b builder] [PARAM n ARRAY{parserNode}] [RETURNS ANY] 13 | [DEFINE i 0] 14 | [DEFINE body [MAKE ARRAY{node}]] 15 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR n]]] 16 | [DEFINE r [buildStmt [VAR b] [INDEX [VAR n] [VAR i]]]] 17 | [IF [CANCAST [VAR r] error] 18 | [RETURN [VAR r]] 19 | ] 20 | [APPEND [VAR body] [CAST [VAR r] node]] 21 | [DEFINE i [MATH [VAR i] + 1]] 22 | ] 23 | 24 | # Check for errors 25 | [IF [COMPARE [LENGTH [GET [VAR b] errors]] > 0] 26 | [RETURN [ERROR [CONCAT "built with " [STRING [LENGTH [GET [VAR b] errors]]] " errors"]]] 27 | ] 28 | 29 | [DEFINE ir [MAKE ir]] 30 | [SET [VAR ir] body [VAR body]] 31 | [RETURN [ANY [VAR ir]]] 32 | ] -------------------------------------------------------------------------------- /backends/cgen/scope.go: -------------------------------------------------------------------------------- 1 | package cgen 2 | 3 | type scope struct { 4 | function bool 5 | vals []string 6 | } 7 | 8 | type stack struct { 9 | vals []scope 10 | } 11 | 12 | func (s *stack) Push(function ...bool) { 13 | if len(function) > 0 { 14 | s.vals = append(s.vals, scope{function: true}) 15 | return 16 | } 17 | s.vals = append(s.vals, scope{}) 18 | } 19 | 20 | func (s *stack) Pop() { 21 | s.vals = s.vals[:len(s.vals)-1] 22 | } 23 | 24 | func (s *stack) FreeCode() string { 25 | return JoinCode(s.vals[len(s.vals)-1].vals...) 26 | } 27 | 28 | func (s *stack) FnFreeCode() string { 29 | // Get free code for everything in function 30 | lastFn := -1 31 | for i, v := range s.vals { 32 | if v.function { 33 | lastFn = i 34 | } 35 | } 36 | out := "" 37 | for i := lastFn; i < len(s.vals); i++ { 38 | out = JoinCode(append([]string{out}, s.vals[i].vals...)...) 39 | } 40 | return out 41 | } 42 | 43 | func (s *stack) Add(val string) { 44 | s.vals[len(s.vals)-1].vals = append(s.vals[len(s.vals)-1].vals, val) 45 | } 46 | -------------------------------------------------------------------------------- /old/ssa/pipeline/main.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/old/ssa" 5 | "github.com/Nv7-Github/bsharp/old/ssa/constrm" 6 | "github.com/Nv7-Github/bsharp/old/ssa/dce" 7 | "github.com/Nv7-Github/bsharp/old/ssa/memrm" 8 | ) 9 | 10 | type Pipeline struct { 11 | constrm bool 12 | dce bool 13 | } 14 | 15 | func New() *Pipeline { 16 | return &Pipeline{} 17 | } 18 | 19 | func (p *Pipeline) ConstantPropagation() { 20 | p.constrm = true 21 | } 22 | 23 | func (p *Pipeline) DeadCodeElimination() { 24 | p.constrm = true // required for DCE 25 | p.dce = true 26 | } 27 | 28 | func (p *Pipeline) Run(s *ssa.SSA) { 29 | // Apply to every function 30 | for _, fn := range s.Funcs { 31 | p.Run(fn) 32 | } 33 | 34 | // Phi removal 35 | memrm := memrm.NewMemRM(s) 36 | memrm.Eval() 37 | 38 | // Constant folding and propagation 39 | if p.constrm { 40 | constrm.Constrm(s) // Constants 41 | constrm.Phirm(s) // Constant phi nodes 42 | } 43 | 44 | if p.dce { 45 | dce := dce.NewDCE(s) 46 | dce.Remove() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /backends/bstar/stmts.go: -------------------------------------------------------------------------------- 1 | package bstar 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Nv7-Github/bsharp/ir" 7 | "github.com/Nv7-Github/bsharp/types" 8 | ) 9 | 10 | func (b *BStar) buildNode(node ir.Node) (Node, error) { 11 | switch n := node.(type) { 12 | case *ir.CallNode: 13 | return b.buildCall(n) 14 | 15 | case *ir.Const: 16 | if n.Type().Equal(types.STRING) { 17 | return constNode(fmt.Sprintf("%q", n.Value)), nil 18 | } 19 | return constNode(n.Value), nil 20 | 21 | case *ir.BlockNode: 22 | return b.buildBlock(n) 23 | 24 | case *ir.CastNode: 25 | return b.addCast(n) 26 | 27 | case *ir.FnCallNode: 28 | return b.buildFnCall(n) 29 | 30 | case *ir.ExtensionCall: 31 | switch n.Name { 32 | case "ARGS": 33 | ind, err := b.buildNode(n.Args[0]) 34 | if err != nil { 35 | return nil, err 36 | } 37 | return blockNode(true, constNode("ARGS"), ind), nil 38 | 39 | default: 40 | return nil, n.Pos().Error("unknown extension call: %s", n.Name) 41 | } 42 | 43 | default: 44 | return nil, n.Pos().Error("unknown node: %T", n) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /bsharp-lsp/build.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "path/filepath" 5 | "strings" 6 | 7 | "github.com/Nv7-Github/bsharp/bot" 8 | "github.com/Nv7-Github/bsharp/ir" 9 | "github.com/Nv7-Github/bsharp/tokens" 10 | ) 11 | 12 | func tokenizeDoc(doc *Document, uri, text string) { 13 | // Tokenize 14 | tok := tokens.NewTokenizer(tokens.NewStream(strings.TrimPrefix(uri, RootURI+"/"), text)) 15 | err := tok.Tokenize() 16 | if err != nil { 17 | return 18 | } 19 | doc.Tokens = tok 20 | updateDocTokenCache(doc) 21 | } 22 | 23 | func getBld(doc *Document) *ir.Builder { 24 | bld := ir.NewBuilder() 25 | if doc.Config.DiscordSupport { 26 | for _, ext := range bot.Exts { 27 | bld.AddExtension(ext) 28 | } 29 | } 30 | return bld 31 | } 32 | 33 | func buildDoc(doc *Document, uri string) { 34 | path := strings.TrimPrefix(uri, "file://") 35 | fs := &FS{} 36 | p, err := fs.Parse(filepath.Base(path)) 37 | if err == nil { 38 | bld := getBld(doc) 39 | err = bld.Build(p, fs) 40 | if err == nil { // No error, save IR cache 41 | doc.IRCache = bld.IR() 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/out/test/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":";;;AAAA;;;gGAGgG;AAChG,6BAA6B;AAC7B,+BAA+B;AAC/B,6BAA6B;AAE7B,SAAgB,GAAG;IAClB,wBAAwB;IACxB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;QACvB,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,IAAI;KACX,CAAC,CAAC;IACH,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAEtB,MAAM,SAAS,GAAG,SAAS,CAAC;IAE5B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,IAAI,CAAC,YAAY,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;YACrD,IAAI,GAAG,EAAE;gBACR,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;aACnB;YAED,8BAA8B;YAC9B,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAE9D,IAAI;gBACH,qBAAqB;gBACrB,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;oBACpB,IAAI,QAAQ,GAAG,CAAC,EAAE;wBACjB,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,QAAQ,gBAAgB,CAAC,CAAC,CAAC;qBAC/C;yBAAM;wBACN,OAAO,EAAE,CAAC;qBACV;gBACF,CAAC,CAAC,CAAC;aACH;YAAC,OAAO,GAAG,EAAE;gBACb,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACnB,MAAM,CAAC,GAAG,CAAC,CAAC;aACZ;QACF,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC;AAlCD,kBAkCC"} -------------------------------------------------------------------------------- /old/ssa/memrm/memrm.go: -------------------------------------------------------------------------------- 1 | // Package memrm turns variable instructions into PHI nodes, algorithm based on https://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf 2 | package memrm 3 | 4 | import "github.com/Nv7-Github/bsharp/old/ssa" 5 | 6 | type BlockData struct { 7 | Filled bool 8 | Variables map[int]ssa.ID 9 | } 10 | 11 | func NewBlockData() *BlockData { 12 | return &BlockData{ 13 | Filled: false, 14 | Variables: make(map[int]ssa.ID), 15 | } 16 | } 17 | 18 | type MemRM struct { 19 | ssa *ssa.SSA 20 | 21 | blockData map[string]*BlockData 22 | } 23 | 24 | func NewMemRM(ssa *ssa.SSA) *MemRM { 25 | m := &MemRM{ 26 | ssa: ssa, 27 | blockData: make(map[string]*BlockData), 28 | } 29 | for k := range m.ssa.Blocks { 30 | m.blockData[k] = NewBlockData() 31 | } 32 | return m 33 | } 34 | 35 | func (m *MemRM) Eval() { 36 | todo := []string{m.ssa.EntryBlock} 37 | for len(todo) > 0 { 38 | t := todo[0] 39 | blk := m.ssa.Blocks[t] 40 | todo = todo[1:] 41 | done := m.evalBlock(t) 42 | if !done { 43 | todo = append(todo, blk.After()...) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Nv7-Github/bsharp 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/Nv7-Github/sevcord v0.0.0-20220730050849-c1404889e919 7 | github.com/alexflint/go-arg v1.4.3 8 | github.com/davecgh/go-spew v1.1.1 9 | github.com/tliron/glsp v0.1.1 10 | github.com/tliron/kutil v0.1.57 11 | ) 12 | 13 | require ( 14 | github.com/alexflint/go-scalar v1.1.0 // indirect 15 | github.com/bwmarrin/discordgo v0.23.3-0.20220428212307-9e0783c37f5d // indirect 16 | github.com/gorilla/websocket v1.5.0 // indirect 17 | github.com/mattn/go-colorable v0.1.12 // indirect 18 | github.com/mattn/go-isatty v0.0.14 // indirect 19 | github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect 20 | github.com/pkg/errors v0.9.1 // indirect 21 | github.com/sasha-s/go-deadlock v0.3.1 // indirect 22 | github.com/sourcegraph/jsonrpc2 v0.1.0 // indirect 23 | github.com/zchee/color/v2 v2.0.6 // indirect 24 | golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect 25 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect 26 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect 27 | ) 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Nv7 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 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Nv7 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. -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | import * as path from 'path'; 6 | 7 | import { runTests } from '@vscode/test-electron'; 8 | 9 | async function main() { 10 | try { 11 | // The folder containing the Extension Manifest package.json 12 | // Passed to `--extensionDevelopmentPath` 13 | const extensionDevelopmentPath = path.resolve(__dirname, '../../../'); 14 | 15 | // The path to test runner 16 | // Passed to --extensionTestsPath 17 | const extensionTestsPath = path.resolve(__dirname, './index'); 18 | 19 | // Download VS Code, unzip it and run the integration test 20 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 21 | } catch (err) { 22 | console.error('Failed to run tests'); 23 | process.exit(1); 24 | } 25 | } 26 | 27 | main(); 28 | -------------------------------------------------------------------------------- /old/ssa/ssagen/var.go: -------------------------------------------------------------------------------- 1 | package ssagen 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | "github.com/Nv7-Github/bsharp/old/ssa" 6 | "github.com/Nv7-Github/bsharp/tokens" 7 | ) 8 | 9 | func (s *SSAGen) addDefine(pos *tokens.Pos, n *ir.DefineNode) ssa.ID { 10 | v := s.Add(n.Value) 11 | va := s.ir.Variables[n.Var] 12 | if va.NeedsGlobal { 13 | return s.blk.AddInstruction(&ssa.GlobalSetVariable{ 14 | Variable: n.Var, 15 | Value: v, 16 | }, pos) 17 | } 18 | return s.blk.AddInstruction(&ssa.SetVariable{ 19 | Variable: n.Var, 20 | Value: v, 21 | }, pos) 22 | } 23 | 24 | func (s *SSAGen) addVar(pos *tokens.Pos, n *ir.VarNode) ssa.ID { 25 | v := s.ir.Variables[n.ID] 26 | if s.fn != nil { 27 | isParam := true 28 | for _, par := range s.fn.Params { 29 | if par.ID != n.ID { 30 | isParam = false 31 | break 32 | } 33 | } 34 | if isParam { 35 | return s.blk.AddInstruction(&ssa.GetParam{ 36 | Variable: n.ID, 37 | }, pos) 38 | } 39 | } 40 | if v.NeedsGlobal { 41 | return s.blk.AddInstruction(&ssa.GlobalGetVariable{ 42 | Variable: n.ID, 43 | }, pos) 44 | } 45 | return s.blk.AddInstruction(&ssa.GetVariable{ 46 | Variable: n.ID, 47 | }, pos) 48 | } 49 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/out/test/completion.test.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"completion.test.js","sourceRoot":"","sources":["../../src/test/completion.test.ts"],"names":[],"mappings":";AAAA;;;gGAGgG;;AAEhG,iCAAiC;AACjC,iCAAiC;AACjC,qCAA+C;AAE/C,KAAK,CAAC,sBAAsB,EAAE,GAAG,EAAE;IAClC,MAAM,MAAM,GAAG,IAAA,kBAAS,EAAC,gBAAgB,CAAC,CAAC;IAE3C,IAAI,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC9C,MAAM,cAAc,CAAC,MAAM,EAAE,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;YACvD,KAAK,EAAE;gBACN,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE;gBAC7D,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE;aAC7D;SACD,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,KAAK,UAAU,cAAc,CAC5B,MAAkB,EAClB,QAAyB,EACzB,sBAA6C;IAE7C,MAAM,IAAA,iBAAQ,EAAC,MAAM,CAAC,CAAC;IAEvB,iGAAiG;IACjG,MAAM,oBAAoB,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,cAAc,CACjE,sCAAsC,EACtC,MAAM,EACN,QAAQ,CACR,CAA0B,CAAC;IAE5B,MAAM,CAAC,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;IAClD,sBAAsB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE;QACxD,MAAM,UAAU,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QACnD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACJ,CAAC"} -------------------------------------------------------------------------------- /backends/interpreter/extensions.go: -------------------------------------------------------------------------------- 1 | package interpreter 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | "github.com/Nv7-Github/bsharp/types" 6 | ) 7 | 8 | type Extension struct { 9 | Name string 10 | Fn func([]any) (any, error) // reflect.FuncOf 11 | ParTypes []types.Type 12 | RetType types.Type 13 | } 14 | 15 | func (e *Extension) IRExtension() *ir.Extension { 16 | return &ir.Extension{ 17 | Name: e.Name, 18 | Params: e.ParTypes, 19 | RetType: e.RetType, 20 | } 21 | } 22 | 23 | func NewExtension(name string, fn func([]any) (any, error), parTypes []types.Type, retTyp types.Type) *Extension { 24 | return &Extension{ 25 | Fn: fn, 26 | Name: name, 27 | ParTypes: parTypes, 28 | RetType: retTyp, 29 | } 30 | } 31 | func (i *Interpreter) evalExtensionCall(n *ir.ExtensionCall) (*Value, error) { 32 | ext := i.extensions[n.Name] 33 | // Build args 34 | args := make([]any, len(n.Args)) 35 | for ind, arg := range n.Args { 36 | val, err := i.evalNode(arg) 37 | if err != nil { 38 | return nil, err 39 | } 40 | args[ind] = val.Value 41 | } 42 | // Call 43 | res, err := ext.Fn(args) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | return NewValue(ext.RetType, res), nil 49 | } 50 | -------------------------------------------------------------------------------- /old/ssa/constrm/phirm.go: -------------------------------------------------------------------------------- 1 | package constrm 2 | 3 | import "github.com/Nv7-Github/bsharp/old/ssa" 4 | 5 | func Phirm(s *ssa.SSA) { 6 | todo := []string{s.EntryBlock} 7 | done := make(map[string]struct{}) 8 | for len(todo) > 0 { 9 | t := todo[0] 10 | blk := s.Blocks[t] 11 | todo = todo[1:] 12 | _, exists := done[blk.Label] 13 | if !exists { 14 | todo = append(todo, blk.After()...) 15 | done[blk.Label] = struct{}{} 16 | } 17 | 18 | // Constant phi removal 19 | for _, id := range blk.Order { 20 | instr := blk.Instructions[id] 21 | p, ok := instr.(*ssa.Phi) 22 | if ok { 23 | // Check if const 24 | isConst := true 25 | for _, val := range p.Values { 26 | b := s.Blocks[s.Instructions[val].Block] 27 | _, ok := b.Instructions[val].(*ssa.Const) 28 | if !ok { 29 | isConst = false 30 | break 31 | } 32 | } 33 | 34 | if isConst { 35 | isSame := true 36 | first := globalcnst(s, p.Values[0]) 37 | for _, v := range p.Values[1:] { 38 | if globalcnst(s, v).Value != first.Value { 39 | isSame = false 40 | break 41 | } 42 | } 43 | 44 | if isSame { 45 | blk.Instructions[id] = first 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tokens/tokenizer.go: -------------------------------------------------------------------------------- 1 | package tokens 2 | 3 | import "fmt" 4 | 5 | type TokenType int 6 | 7 | const ( 8 | TokenTypeIdent TokenType = iota 9 | TokenTypeNumber 10 | TokenTypeString 11 | TokenTypeByte 12 | TokenTypeLBrack 13 | TokenTypeRBrack 14 | ) 15 | 16 | func (t TokenType) String() string { 17 | return [...]string{"Ident", "Number", "String", "LBrack", "RBrack"}[t] 18 | } 19 | 20 | type Token struct { 21 | Typ TokenType 22 | Value string 23 | Pos *Pos 24 | } 25 | 26 | func (t Token) String() string { 27 | return fmt.Sprintf("Token(%s, \"%s\", %s)", t.Typ.String(), t.Value, t.Pos.String()) 28 | } 29 | 30 | type Tokenizer struct { 31 | Tokens []Token 32 | pos int 33 | s *Stream 34 | } 35 | 36 | func NewTokenizer(s *Stream) *Tokenizer { 37 | return &Tokenizer{ 38 | Tokens: make([]Token, 0), 39 | s: s, 40 | } 41 | } 42 | 43 | func (t *Tokenizer) HasNext() bool { 44 | return t.pos < len(t.Tokens) 45 | } 46 | 47 | func (t *Tokenizer) Tok() Token { 48 | return t.Tokens[t.pos] 49 | } 50 | 51 | func (t *Tokenizer) Eat() { 52 | t.pos++ 53 | } 54 | 55 | func (t *Tokenizer) Last() *Pos { 56 | tok := t.Tokens[len(t.Tokens)-1] 57 | p := tok.Pos.Dup() 58 | p.Char += len(tok.Value) 59 | return p 60 | } 61 | 62 | func (t *Tokenizer) Filename() string { 63 | return t.s.file 64 | } 65 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/out/test/helper.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"helper.js","sourceRoot":"","sources":["../../src/test/helper.ts"],"names":[],"mappings":";AAAA;;;gGAGgG;;;AAEhG,iCAAiC;AACjC,6BAA6B;AAO7B;;GAEG;AACI,KAAK,UAAU,QAAQ,CAAC,MAAkB;IAChD,wDAAwD;IACxD,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,2BAA2B,CAAE,CAAC;IACzE,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC;IACrB,IAAI;QACH,WAAG,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;QACtD,cAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,WAAG,CAAC,CAAC;QACnD,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,6BAA6B;KAChD;IAAC,OAAO,CAAC,EAAE;QACX,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KACjB;AACF,CAAC;AAXD,4BAWC;AAED,KAAK,UAAU,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC;AAEM,MAAM,UAAU,GAAG,CAAC,CAAS,EAAE,EAAE;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC,CAAC;AAFW,QAAA,UAAU,cAErB;AACK,MAAM,SAAS,GAAG,CAAC,CAAS,EAAE,EAAE;IACtC,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAA,kBAAU,EAAC,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC,CAAC;AAFW,QAAA,SAAS,aAEpB;AAEK,KAAK,UAAU,cAAc,CAAC,OAAe;IACnD,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,KAAK,CAC3B,WAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EACjB,WAAG,CAAC,UAAU,CAAC,WAAG,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CACpC,CAAC;IACF,OAAO,cAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;AACpD,CAAC;AAND,wCAMC"} -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/out/test/runTest.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | /*--------------------------------------------------------------------------------------------- 4 | * Copyright (c) Microsoft Corporation. All rights reserved. 5 | * Licensed under the MIT License. See License.txt in the project root for license information. 6 | *--------------------------------------------------------------------------------------------*/ 7 | const path = require("path"); 8 | const test_electron_1 = require("@vscode/test-electron"); 9 | async function main() { 10 | try { 11 | // The folder containing the Extension Manifest package.json 12 | // Passed to `--extensionDevelopmentPath` 13 | const extensionDevelopmentPath = path.resolve(__dirname, '../../../'); 14 | // The path to test runner 15 | // Passed to --extensionTestsPath 16 | const extensionTestsPath = path.resolve(__dirname, './index'); 17 | // Download VS Code, unzip it and run the integration test 18 | await (0, test_electron_1.runTests)({ extensionDevelopmentPath, extensionTestsPath }); 19 | } 20 | catch (err) { 21 | console.error('Failed to run tests'); 22 | process.exit(1); 23 | } 24 | } 25 | main(); 26 | //# sourceMappingURL=runTest.js.map -------------------------------------------------------------------------------- /backends/cgen/var.go: -------------------------------------------------------------------------------- 1 | package cgen 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/Nv7-Github/bsharp/ir" 8 | ) 9 | 10 | func (c *CGen) addDefine(n *ir.DefineNode) (*Code, error) { 11 | v := c.ir.Variables[n.Var] 12 | name := Namespace + v.Name + strconv.Itoa(v.ID) 13 | val, err := c.AddNode(n.Value) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | pre := val.Pre 19 | free := "" 20 | if isDynamic(v.Type) { // Free old val, grab new one 21 | pre = JoinCode(pre, c.GrabCode(val.Value, n.Value.Type())) 22 | if c.declaredVars[v.ID] { 23 | oldName := c.GetTmp("old") 24 | pre = JoinCode(fmt.Sprintf("%s %s = %s;", c.CType(v.Type), oldName, name), pre) 25 | free = c.FreeCode(oldName, v.Type) 26 | } 27 | } 28 | 29 | code := fmt.Sprintf("%s = %s;", name, val.Value) 30 | // If not declared, then declare 31 | if !c.declaredVars[v.ID] { 32 | // Check if it is global 33 | if v.NeedsGlobal { 34 | fmt.Fprintf(c.globals, "%s %s;\n", c.CType(v.Type), name) 35 | c.declaredVars[v.ID] = true 36 | } else { 37 | code = fmt.Sprintf("%s %s = %s;", c.CType(v.Type), name, val.Value) 38 | c.declaredVars[v.ID] = true 39 | } 40 | 41 | if isDynamic(v.Type) { 42 | c.stack.Add(c.FreeCode(name, v.Type)) 43 | } 44 | } 45 | 46 | return &Code{ 47 | Pre: JoinCode(pre, code, free), 48 | }, nil 49 | } 50 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/src/test/index.ts: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | * ------------------------------------------------------------------------------------------ */ 5 | import * as path from 'path'; 6 | import * as Mocha from 'mocha'; 7 | import * as glob from 'glob'; 8 | 9 | export function run(): Promise { 10 | // Create the mocha test 11 | const mocha = new Mocha({ 12 | ui: 'tdd', 13 | color: true 14 | }); 15 | mocha.timeout(100000); 16 | 17 | const testsRoot = __dirname; 18 | 19 | return new Promise((resolve, reject) => { 20 | glob('**.test.js', { cwd: testsRoot }, (err, files) => { 21 | if (err) { 22 | return reject(err); 23 | } 24 | 25 | // Add files to the test suite 26 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 27 | 28 | try { 29 | // Run the mocha test 30 | mocha.run(failures => { 31 | if (failures > 0) { 32 | reject(new Error(`${failures} tests failed.`)); 33 | } else { 34 | resolve(); 35 | } 36 | }); 37 | } catch (err) { 38 | console.error(err); 39 | reject(err); 40 | } 41 | }); 42 | }); 43 | } -------------------------------------------------------------------------------- /std/math.bsp: -------------------------------------------------------------------------------- 1 | [FUNC FLOOR [PARAM val FLOAT] [RETURNS INT] 2 | [RETURN [INT [MATH [VAR val] - [MATH [VAR val] % 1]]]] # val % 1 is the decimal part, subtracts it from the value to get the floor 3 | ] 4 | 5 | [FUNC CEIL [PARAM val FLOAT] [RETURNS INT] 6 | [RETURN [INT [MATH [VAR val] + [MATH 1 - [MATH [VAR val] % 1]]]]] # works similar to FLOOR 7 | ] 8 | 9 | [FUNC ROUND [PARAM val FLOAT] [RETURNS INT] 10 | [IF [COMPARE [VAR val] >= 0.0] 11 | [RETURN [FLOOR [MATH [VAR val] + 0.5]]] 12 | ] 13 | [RETURN [CEIL [MATH [VAR val] - 0.5]]] 14 | ] 15 | 16 | [DEFINE seed [MATH [TIME NANO] % 2147483647]] 17 | 18 | # This uses the glibc random values, see https://en.wikipedia.org/wiki/Linear_congruential_generator 19 | 20 | [FUNC RANDINT [PARAM lower INT] [PARAM upper INT] [RETURNS INT] 21 | [DEFINE seed [MATH [MATH [MATH 1103515245 * [VAR seed]] + 12345] % 2147483647]] # Glibc rand algorithm 22 | [RETURN [MATH [MATH [VAR seed] % [MATH [VAR upper] - [VAR lower]]] + [VAR lower]]] 23 | ] 24 | 25 | [FUNC RANDOM [PARAM lower FLOAT] [PARAM upper FLOAT] [RETURNS FLOAT] 26 | [DEFINE seed [MATH [MATH [MATH 1103515245 * [VAR seed]] + 12345] % 2147483647]] # Glibc rand algorithm 27 | # Get val from 0..1 28 | [DEFINE val [MATH [FLOAT [VAR seed]] / 2147483647.0]] 29 | [RETURN [MATH [VAR lower] + [MATH [VAR val] * [MATH [VAR upper] - [VAR lower]]]]] 30 | ] -------------------------------------------------------------------------------- /backends/interpreter/fns.go: -------------------------------------------------------------------------------- 1 | package interpreter 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | "github.com/Nv7-Github/bsharp/types" 6 | ) 7 | 8 | func (i *Interpreter) evalCallNode(n *ir.FnCallNode) (*Value, error) { 9 | fnName, err := i.evalNode(n.Fn) 10 | if err != nil { 11 | return nil, err 12 | } 13 | 14 | fn := i.ir.Funcs[fnName.Value.(string)] 15 | // Push to stack 16 | i.stack.Push() 17 | 18 | // Build args 19 | args, err := i.evalNodes(n.Params) 20 | if err != nil { 21 | return nil, err 22 | } 23 | for ind, par := range fn.Params { 24 | i.stack.Set(par.ID, args[ind], true) 25 | } 26 | // Run 27 | for _, v := range fn.Body { 28 | _, err = i.evalNode(v) 29 | if err != nil { 30 | return nil, err 31 | } 32 | } 33 | retVal := i.retVal 34 | if types.NULL.Equal(fn.RetType) { 35 | retVal = NewValue(types.NULL, nil) 36 | } 37 | i.retVal = nil // Un-return 38 | 39 | // Pop stack 40 | i.stack.Pop() 41 | return retVal, nil 42 | } 43 | 44 | func (i *Interpreter) evalReturnNode(n *ir.ReturnNode) (*Value, error) { 45 | v, err := i.evalNode(n.Value) 46 | if err != nil { 47 | return nil, err 48 | } 49 | i.retVal = v 50 | return nil, nil 51 | } 52 | 53 | func (i *Interpreter) evalFnNode(n *ir.FnNode) (*Value, error) { 54 | return &Value{ 55 | Type: n.Type(), 56 | Value: n.Name, 57 | }, nil 58 | } 59 | -------------------------------------------------------------------------------- /backends/interpreter/interpreter.go: -------------------------------------------------------------------------------- 1 | package interpreter 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Nv7-Github/bsharp/ir" 7 | "github.com/Nv7-Github/bsharp/types" 8 | ) 9 | 10 | type Interpreter struct { 11 | ir *ir.IR 12 | 13 | stdout io.Writer 14 | stack *stack 15 | extensions map[string]*Extension 16 | 17 | retVal *Value 18 | stopMsg *string 19 | } 20 | 21 | type Value struct { 22 | Type types.Type 23 | Value any 24 | } 25 | 26 | func NewValue(typ types.Type, val any) *Value { 27 | return &Value{ 28 | Type: typ, 29 | Value: val, 30 | } 31 | } 32 | 33 | func NewInterpreter(ir *ir.IR, stdout io.Writer) *Interpreter { 34 | return &Interpreter{ 35 | ir: ir, 36 | stdout: stdout, 37 | extensions: make(map[string]*Extension), 38 | stack: &stack{ 39 | vals: make([]scope, 0), 40 | }, 41 | } 42 | } 43 | 44 | func (i *Interpreter) AddExtension(e *Extension) { 45 | i.extensions[e.Name] = e 46 | } 47 | 48 | func (i *Interpreter) SetStdout(stdout io.Writer) { 49 | i.stdout = stdout 50 | } 51 | 52 | func (i *Interpreter) Run() error { 53 | i.stack.Push() 54 | for _, node := range i.ir.Body { 55 | if _, err := i.evalNode(node); err != nil { 56 | return err 57 | } 58 | } 59 | i.stack.Pop() 60 | return nil 61 | } 62 | 63 | func (i *Interpreter) Stop(msg string) { 64 | i.stopMsg = &msg 65 | } 66 | -------------------------------------------------------------------------------- /old/ssa/consts.go: -------------------------------------------------------------------------------- 1 | package ssa 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/Nv7-Github/bsharp/ir" 8 | "github.com/Nv7-Github/bsharp/types" 9 | ) 10 | 11 | type Concat struct { 12 | Values []ID 13 | } 14 | 15 | func (c *Concat) Type() types.Type { return types.STRING } 16 | func (c *Concat) String() string { 17 | out := &strings.Builder{} 18 | out.WriteString("Concat (") 19 | for i, val := range c.Values { 20 | out.WriteString(val.String()) 21 | if i != len(c.Values)-1 { 22 | out.WriteString(", ") 23 | } 24 | } 25 | out.WriteRune(')') 26 | return out.String() 27 | } 28 | func (c *Concat) Args() []ID { return c.Values } 29 | func (c *Concat) SetArgs(v []ID) { c.Values = v } 30 | 31 | type LogicalOp struct { 32 | Op ir.LogicalOp 33 | Lhs ID 34 | Rhs *ID // nil if no rhs 35 | } 36 | 37 | func (l *LogicalOp) Type() types.Type { return types.BOOL } 38 | func (l *LogicalOp) String() string { 39 | if l.Rhs != nil { 40 | return fmt.Sprintf("%s (%s, %s)", l.Op.String(), l.Lhs.String(), l.Rhs.String()) 41 | } 42 | return fmt.Sprintf("%s %s", l.Op.String(), l.Lhs.String()) 43 | } 44 | func (l *LogicalOp) Args() []ID { 45 | if l.Rhs != nil { 46 | return []ID{l.Lhs, *l.Rhs} 47 | } 48 | return []ID{l.Lhs} 49 | } 50 | func (l *LogicalOp) SetArgs(v []ID) { 51 | l.Lhs = v[0] 52 | if len(v) > 1 { 53 | l.Rhs = &v[1] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /old/ssa/fn.go: -------------------------------------------------------------------------------- 1 | package ssa 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/Nv7-Github/bsharp/types" 8 | ) 9 | 10 | type FnCall struct { 11 | Fn ID 12 | Params []ID 13 | Typ types.Type 14 | } 15 | 16 | func (f *FnCall) Type() types.Type { return f.Typ } 17 | func (f *FnCall) String() string { 18 | out := &strings.Builder{} 19 | fmt.Fprintf(out, "FnCall [%s](", f.Fn.String()) 20 | for i, val := range f.Params { 21 | out.WriteString(val.String()) 22 | if i != len(f.Params)-1 { 23 | out.WriteString(", ") 24 | } 25 | } 26 | out.WriteRune(')') 27 | return out.String() 28 | } 29 | func (f *FnCall) Args() []ID { return append([]ID{f.Fn}, f.Params...) } 30 | func (f *FnCall) SetArgs(args []ID) { 31 | f.Fn = args[0] 32 | f.Params = args[1:] 33 | } 34 | 35 | type ExtensionCall struct { 36 | Fn string 37 | Params []ID 38 | Typ types.Type 39 | } 40 | 41 | func (e *ExtensionCall) Type() types.Type { return e.Typ } 42 | func (e *ExtensionCall) String() string { 43 | out := &strings.Builder{} 44 | fmt.Fprintf(out, "FnCall [%s](", e.Fn) 45 | for i, val := range e.Params { 46 | out.WriteString(val.String()) 47 | if i != len(e.Params)-1 { 48 | out.WriteString(", ") 49 | } 50 | } 51 | out.WriteRune(')') 52 | return out.String() 53 | } 54 | func (e *ExtensionCall) Args() []ID { return e.Params } 55 | func (e *ExtensionCall) SetArgs(args []ID) { e.Params = args } 56 | -------------------------------------------------------------------------------- /backends/interpreter/basic.go: -------------------------------------------------------------------------------- 1 | package interpreter 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | "github.com/Nv7-Github/bsharp/ir" 8 | "github.com/Nv7-Github/bsharp/tokens" 9 | "github.com/Nv7-Github/bsharp/types" 10 | ) 11 | 12 | func (i *Interpreter) evalPrint(c *ir.PrintNode) error { 13 | v, err := i.evalNode(c.Arg) 14 | if err != nil { 15 | return err 16 | } 17 | _, err = i.stdout.Write([]byte(v.Value.(string) + "\n")) 18 | return err 19 | } 20 | 21 | func (i *Interpreter) evalPanic(c *ir.PanicNode, pos *tokens.Pos) error { 22 | v, err := i.evalNode(c.Arg) 23 | if err != nil { 24 | return err 25 | } 26 | return pos.Error("%s", v.Value.(string)) 27 | } 28 | 29 | func (i *Interpreter) evalConcat(c *ir.ConcatNode) (*Value, error) { 30 | out := &strings.Builder{} 31 | for _, arg := range c.Values { 32 | v, err := i.evalNode(arg) 33 | if err != nil { 34 | return nil, err 35 | } 36 | out.WriteString(v.Value.(string)) 37 | } 38 | return NewValue(types.STRING, out.String()), nil 39 | } 40 | 41 | func (i *Interpreter) evalTime(c *ir.TimeNode) *Value { 42 | now := time.Now() 43 | var out int64 44 | switch c.Mode { 45 | case ir.TimeModeSeconds: 46 | out = now.Unix() 47 | 48 | case ir.TimeModeMicro: 49 | out = now.UnixMicro() 50 | 51 | case ir.TimeModeMilli: 52 | out = now.UnixMilli() 53 | 54 | case ir.TimeModeNano: 55 | out = now.UnixNano() 56 | } 57 | return NewValue(types.INT, int(out)) 58 | } 59 | -------------------------------------------------------------------------------- /std/json.bsp: -------------------------------------------------------------------------------- 1 | [FUNC ENCODE [PARAM value ANY] [RETURNS STRING] 2 | [IF [CANCAST [VAR value] MAP{STRING, ANY}] 3 | [DEFINE out "{"] 4 | [DEFINE v [CAST [VAR value] MAP{STRING, ANY}]] 5 | [DEFINE keys [KEYS [VAR v]]] 6 | [DEFINE i 0] 7 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR keys]]] 8 | [IF [COMPARE [VAR i] > 0] 9 | [DEFINE out [CONCAT [VAR out] ","]] 10 | ] 11 | [DEFINE k [INDEX [VAR keys] [VAR i]]] 12 | [DEFINE out [CONCAT [VAR out] "\"" [VAR k] "\":" [ENCODE [GET [VAR v] [VAR k]]]]] 13 | [DEFINE i [MATH [VAR i] + 1]] 14 | ] 15 | [RETURN [CONCAT [VAR out] "}"]] 16 | ] 17 | [IF [CANCAST [VAR value] STRING] 18 | [RETURN [CONCAT "\"" [CAST [VAR value] STRING] "\""]] 19 | ] 20 | [IF [CANCAST [VAR value] INT] 21 | [RETURN [CONCAT [STRING [CAST [VAR value] INT]]]] 22 | ] 23 | [IF [CANCAST [VAR value] FLOAT] 24 | [RETURN [CONCAT [STRING [CAST [VAR value] FLOAT]]]] 25 | ] 26 | [IF [CANCAST [VAR value] ARRAY{ANY}] 27 | [DEFINE v [CAST [VAR value] ARRAY{ANY}]] 28 | [DEFINE out "["] 29 | [DEFINE i 0] 30 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR v]]] 31 | [IF [COMPARE [VAR i] > 0] 32 | [DEFINE out [CONCAT [VAR out] ","]] 33 | ] 34 | [DEFINE out [CONCAT [VAR out] [ENCODE [INDEX [VAR v] [VAR i]]]]] 35 | [DEFINE i [MATH [VAR i] + 1]] 36 | ] 37 | [RETURN [CONCAT [VAR out] "]"]] 38 | ] 39 | [RETURN "invalid object"] 40 | ] -------------------------------------------------------------------------------- /bpp/main.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "bpp/parser.bsp"] 2 | [IMPORT "bpp/interp.bsp"] 3 | 4 | # Add builders 5 | [IMPORT "bpp/funcs/blds.bsp"] 6 | 7 | [DEFINE source "[DEFINE s \"toiswcbphfmdrelnagukvyjqzx\"]\n[DEFINE l \"etaoinshrdlucmfwypvbgkjqxz\"]\n[DEFINE length [RANDINT 3 9]]\n[DEFINE x [MATH 26 ^ 0.5]]\n[DEFINE c1 [INDEX [VAR s] [FLOOR [MATH [RANDOM 0 [VAR x]] ^ 2]]]]\n[DEFINE c2 [INDEX [VAR l] [FLOOR [MATH [RANDOM 0 [VAR x]] ^ 2]]]]\n[DEFINE c3 [INDEX [VAR l] [FLOOR [MATH [RANDOM 0 [VAR x]] ^ 2]]]]\n[DEFINE c4 [IF [COMPARE [VAR length] > 3] [INDEX [VAR l] [FLOOR [MATH [RANDOM 0 [VAR x]] ^ 2]]] \"\"]]\n[DEFINE c5 [IF [COMPARE [VAR length] > 4] [INDEX [VAR l] [FLOOR [MATH [RANDOM 0 [VAR x]] ^ 2]]] \"\"]]\n[DEFINE c6 [IF [COMPARE [VAR length] > 5] [INDEX [VAR l] [FLOOR [MATH [RANDOM 0 [VAR x]] ^ 2]]] \"\"]]\n[DEFINE c7 [IF [COMPARE [VAR length] > 6] [INDEX [VAR l] [FLOOR [MATH [RANDOM 0 [VAR x]] ^ 2]]] \"\"]]\n[DEFINE c8 [IF [COMPARE [VAR length] > 7] [INDEX [VAR l] [FLOOR [MATH [RANDOM 0 [VAR x]] ^ 2]]] \"\"]]\nI kin the word **[VAR c1][VAR c2][VAR c3][VAR c4][VAR c5][VAR c6][VAR c7][VAR c8]**.\nPing woooowoooo if the result is a real word. These words use common letters more frequently."] 8 | [DEFINE parsed [PARSE [VAR source]]] # Parse 9 | [DEFINE parsed [INTERP [VAR parsed]]] # Interpret 10 | 11 | # Print 12 | [DEFINE out ""] 13 | [DEFINE i 0] 14 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR parsed]]] 15 | [DEFINE out [CONCAT [VAR out] [NODE_STRING [INDEX [VAR parsed] [VAR i]]]]] 16 | [DEFINE i [MATH [VAR i] + 1]] 17 | ] 18 | [PRINT [VAR out]] -------------------------------------------------------------------------------- /old/ssa/ssagen/ssagen.go: -------------------------------------------------------------------------------- 1 | package ssagen 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | "github.com/Nv7-Github/bsharp/old/ssa" 6 | "github.com/Nv7-Github/bsharp/tokens" 7 | "github.com/Nv7-Github/bsharp/types" 8 | ) 9 | 10 | type SSAGen struct { 11 | ir *ir.IR 12 | ssa *ssa.SSA 13 | blk *ssa.Block 14 | fn *ir.Function 15 | } 16 | 17 | func NewSSAGen(i *ir.IR) *SSAGen { 18 | s := ssa.NewSSA() 19 | b := s.NewBlock("entry", &tokens.Pos{}) 20 | return &SSAGen{ 21 | ir: i, 22 | ssa: s, 23 | blk: b, 24 | } 25 | } 26 | 27 | func (s *SSAGen) Build() { 28 | s.ssa.VariableTypes = make([]types.Type, len(s.ir.Variables)) 29 | for _, v := range s.ir.Variables { 30 | s.ssa.VariableTypes[v.ID] = v.Type 31 | } 32 | 33 | // Build functions 34 | if s.fn == nil { // Don't build if within function 35 | for _, fn := range s.ir.Funcs { 36 | i := &ir.IR{ 37 | Funcs: s.ir.Funcs, 38 | Variables: s.ir.Variables, 39 | Body: fn.Body, 40 | } 41 | gen := NewSSAGen(i) 42 | gen.fn = fn 43 | gen.Build() 44 | ssa := gen.SSA() 45 | s.ssa.Funcs[fn.Name] = ssa 46 | } 47 | } else { 48 | // Build param types 49 | s.ssa.ParamTypes = make([]types.Type, len(s.fn.Params)) 50 | for i, par := range s.fn.Params { 51 | s.ssa.ParamTypes[i] = par.Type 52 | } 53 | } 54 | 55 | // Build body 56 | for _, node := range s.ir.Body { 57 | s.Add(node) 58 | } 59 | s.blk.EndInstructionExit() 60 | s.blk = nil 61 | } 62 | 63 | func (s *SSAGen) SSA() *ssa.SSA { 64 | return s.ssa 65 | } 66 | -------------------------------------------------------------------------------- /bsharp-lsp/sync.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | "github.com/Nv7-Github/bsharp/tokens" 6 | "github.com/tliron/glsp" 7 | protocol "github.com/tliron/glsp/protocol_3_16" 8 | ) 9 | 10 | type Document struct { 11 | Source string 12 | IRCache *ir.IR 13 | Tokens *tokens.Tokenizer 14 | SemanticTokens *protocol.SemanticTokens 15 | Config *Config 16 | } 17 | 18 | var Documents = map[string]*Document{} 19 | 20 | func textDocumentDidOpen(context *glsp.Context, params *protocol.DidOpenTextDocumentParams) error { 21 | doc := &Document{ 22 | Source: params.TextDocument.Text, 23 | } 24 | Documents[params.TextDocument.URI] = doc 25 | 26 | go func() { 27 | doc.Config = config(context, params.TextDocument.URI) 28 | 29 | // Build IR Cache if possible 30 | buildDoc(doc, params.TextDocument.URI) 31 | 32 | // Semantic tokens 33 | tokenizeDoc(doc, params.TextDocument.URI, params.TextDocument.Text) 34 | 35 | }() 36 | 37 | return nil 38 | } 39 | 40 | func textDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error { 41 | delete(Documents, params.TextDocument.URI) 42 | return nil 43 | } 44 | 45 | func textDocumentDidChange(context *glsp.Context, params *protocol.DidChangeTextDocumentParams) error { 46 | doc := Documents[params.TextDocument.URI] 47 | c := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole) 48 | doc.Source = c.Text 49 | tokenizeDoc(doc, params.TextDocument.URI, c.Text) 50 | buildDoc(doc, params.TextDocument.URI) 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /bot/bot.go: -------------------------------------------------------------------------------- 1 | package bot 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/Nv7-Github/bsharp/bot/db" 7 | "github.com/Nv7-Github/sevcord" 8 | ) 9 | 10 | type Bot struct { 11 | *sync.RWMutex 12 | *db.DB 13 | 14 | c *sevcord.Client 15 | } 16 | 17 | func ErrorMessage(ctx sevcord.Ctx, msg string) { 18 | ctx.Respond(sevcord.EmbedResponse(sevcord.NewEmbedBuilder("Error").Color(15548997).Description(msg))) 19 | } 20 | 21 | func Error(ctx sevcord.Ctx, err error) bool { 22 | if err != nil { 23 | ErrorMessage(ctx, err.Error()) 24 | return true 25 | } 26 | return false 27 | } 28 | 29 | func (b *Bot) Autocomplete(ctx sevcord.Ctx, val any) []sevcord.Choice { 30 | db, err := b.Get(ctx.Guild()) 31 | if err != nil { 32 | return nil 33 | } 34 | return db.Autocomplete(val.(string)) 35 | } 36 | 37 | func NewBot(path string, token string) (*Bot, error) { 38 | b := &Bot{ 39 | RWMutex: &sync.RWMutex{}, 40 | } 41 | d, err := db.NewDB(path) 42 | if err != nil { 43 | return nil, err 44 | } 45 | b.DB = d 46 | c, err := sevcord.NewClient(token) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | // Commands 52 | c.HandleSlashCommand(BuildCmd(b)) 53 | c.HandleSlashCommand(CreateCmd(b)) 54 | c.HandleSlashCommand(EditCmd(b)) 55 | c.HandleSlashCommand(DescriptionCmd(b)) 56 | c.HandleSlashCommand(ImageCmd(b)) 57 | c.HandleSlashCommand(InfoCmd(b)) 58 | c.HandleSlashCommand(SourceCmd(b)) 59 | c.HandleSlashCommand(RunCmd(b)) 60 | c.HandleSlashCommand(LbCmd(b)) 61 | c.Start() 62 | return b, nil 63 | } 64 | 65 | func (b *Bot) Close() { 66 | b.DB.Close() 67 | b.c.Close() 68 | } 69 | -------------------------------------------------------------------------------- /ir/extensions.go: -------------------------------------------------------------------------------- 1 | package ir 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/parser" 5 | "github.com/Nv7-Github/bsharp/tokens" 6 | "github.com/Nv7-Github/bsharp/types" 7 | ) 8 | 9 | type Extension struct { 10 | Name string 11 | Params []types.Type 12 | RetType types.Type 13 | } 14 | 15 | type ExtensionCall struct { 16 | Name string 17 | Args []Node 18 | typ types.Type 19 | pos *tokens.Pos 20 | } 21 | 22 | func (e *ExtensionCall) Type() types.Type { return e.typ } 23 | func (e *ExtensionCall) Pos() *tokens.Pos { return e.pos } 24 | 25 | func (b *Builder) buildExtensionCall(n *parser.CallNode) (Node, error) { 26 | ext := b.extensions[n.Name] 27 | 28 | // Add params 29 | args := make([]Node, len(n.Args)) 30 | for i, arg := range n.Args { 31 | node, err := b.buildNode(arg) 32 | if err != nil { 33 | return nil, err 34 | } 35 | args[i] = node 36 | } 37 | err := b.MatchTypes(n.Pos(), args, ext.Params) 38 | if err { 39 | return NewTypedNode(ext.RetType, n.Pos()), nil 40 | } 41 | 42 | return &ExtensionCall{ 43 | Name: n.Name, 44 | Args: args, 45 | typ: ext.RetType, 46 | pos: n.Pos(), 47 | }, nil 48 | } 49 | 50 | type BuiltinFn struct { 51 | Name string 52 | Params []types.Type // nil if block builder 53 | } 54 | 55 | func BuiltinFns() []*BuiltinFn { 56 | out := make([]*BuiltinFn, len(nodeBuilders)+len(blockBuilders)) 57 | i := 0 58 | for name, builder := range nodeBuilders { 59 | out[i] = &BuiltinFn{ 60 | Name: name, 61 | Params: builder.ArgTypes, 62 | } 63 | i++ 64 | } 65 | for name := range blockBuilders { 66 | out[i] = &BuiltinFn{ 67 | Name: name, 68 | } 69 | i++ 70 | } 71 | return out 72 | } 73 | -------------------------------------------------------------------------------- /bot/db/autocomplete.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "sort" 5 | "strings" 6 | 7 | "github.com/Nv7-Github/sevcord" 8 | ) 9 | 10 | const maxResults = 25 11 | 12 | func (d *Data) Autocomplete(query string) []sevcord.Choice { 13 | type searchResult struct { 14 | priority int 15 | uses int 16 | id string 17 | name string 18 | } 19 | results := make([]searchResult, 0) 20 | d.RLock() 21 | for _, prog := range d.Programs { 22 | if strings.EqualFold(prog.Name, query) { 23 | results = append(results, searchResult{0, prog.Uses, prog.ID, prog.Name}) 24 | } else if strings.HasPrefix(strings.ToLower(prog.Name), query) { 25 | results = append(results, searchResult{1, prog.Uses, prog.ID, prog.Name}) 26 | } else if strings.Contains(strings.ToLower(prog.Name), query) { 27 | results = append(results, searchResult{2, prog.Uses, prog.ID, prog.Name}) 28 | } 29 | if len(results) > 1000 { 30 | break 31 | } 32 | } 33 | d.RUnlock() 34 | 35 | // sort by uses 36 | sort.Slice(results, func(i, j int) bool { 37 | return results[i].uses > results[j].uses 38 | }) 39 | 40 | // sort by priority 41 | sort.Slice(results, func(i, j int) bool { 42 | return results[i].priority < results[j].priority 43 | }) 44 | 45 | // return top 25 46 | if len(results) > maxResults { 47 | results = results[:maxResults] 48 | } 49 | 50 | // sort by name 51 | sort.Slice(results, func(i, j int) bool { 52 | return results[i].name < results[j].name 53 | }) 54 | 55 | // Return 56 | out := make([]sevcord.Choice, len(results)) 57 | for i, res := range results { 58 | out[i] = sevcord.Choice{ 59 | Name: res.name, 60 | Value: res.id, 61 | } 62 | } 63 | return out 64 | } 65 | -------------------------------------------------------------------------------- /bsp/ir/ops.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "bsp/ir/ir.bsp"] 2 | 3 | [CONSTDEF mathOpAdd 0] 4 | [CONSTDEF mathOpSub 1] 5 | [CONSTDEF mathOpMul 2] 6 | [CONSTDEF mathOpDiv 3] 7 | [CONSTDEF mathOpPow 4] 8 | [CONSTDEF mathOpMod 5] 9 | 10 | [FUNC addMath [PARAM b builder] [PARAM pos pos] [PARAM args ARRAY{node}] [RETURNS ANY] 11 | [DEFINE op 0] 12 | [DEFINE val [CAST [GET [INDEX [VAR args] 1] val] STRING]] 13 | [SWITCH [VAR val] 14 | [CASE "+" [DEFINE op [CONST mathOpAdd]]] 15 | [CASE "-" [DEFINE op [CONST mathOpSub]]] 16 | [CASE "*" [DEFINE op [CONST mathOpMul]]] 17 | [CASE "/" [DEFINE op [CONST mathOpDiv]]] 18 | [CASE "^" [DEFINE op [CONST mathOpPow]]] 19 | [CASE "%" [DEFINE op [CONST mathOpMod]]] 20 | [DEFAULT 21 | [bldErr [VAR b] [CONST errorLevelError] [VAR pos] [CONCAT "unknown math operation: " [VAR val]]] 22 | ] 23 | ] 24 | 25 | [DEFINE v [MAKE mathNode]] 26 | [SET [VAR v] op [VAR op]] 27 | 28 | [DEFINE lhs [INDEX [VAR args] 0]] 29 | [DEFINE rhs [INDEX [VAR args] 2]] 30 | [IF [NOT [TEQUAL [GET [VAR lhs] typ] [GET [VAR rhs] typ]]] 31 | [bldErr [VAR b] [CONST errorLevelError] [VAR pos] "lhs and rhs must have same type"] 32 | [DEFINE rhs [VAR lhs]] 33 | ] 34 | [SET [VAR v] lhs [VAR lhs]] 35 | [SET [VAR v] rhs [VAR rhs]] 36 | 37 | [DEFINE out [MAKE node]] 38 | [SET [VAR out] typ [GET [VAR lhs] typ]] 39 | [SET [VAR out] val [ANY [VAR v]]] 40 | [SET [VAR out] kind [CONST nodeKindMath]] 41 | [SET [VAR out] pos [VAR pos]] 42 | 43 | [RETURN [ANY [VAR out]]] 44 | ] 45 | 46 | [addNodeBuilder "MATH" [ARRAY [MULTYPE [ARRAY [_INT] [_FLOAT]]] [_IDENT] [MULTYPE [ARRAY [_INT] [_FLOAT]]]] [FN addMath]] -------------------------------------------------------------------------------- /old/ssa/ssagen/ssagen_test.go: -------------------------------------------------------------------------------- 1 | package ssagen 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/Nv7-Github/bsharp/ir" 9 | "github.com/Nv7-Github/bsharp/old/ssa/bspgen" 10 | "github.com/Nv7-Github/bsharp/old/ssa/phirm" 11 | "github.com/Nv7-Github/bsharp/old/ssa/pipeline" 12 | "github.com/Nv7-Github/bsharp/parser" 13 | "github.com/Nv7-Github/bsharp/tokens" 14 | "github.com/davecgh/go-spew/spew" 15 | ) 16 | 17 | const code = `# SSAGen Test 18 | [DEFINE i 0] 19 | [WHILE [COMPARE [VAR i] < 10] 20 | [PRINT [STRING [VAR i]]] 21 | [DEFINE i [MATH [VAR i] + 1]] 22 | ] 23 | ` 24 | 25 | type fs struct{} 26 | 27 | func (*fs) Parse(f string) (*parser.Parser, error) { 28 | return nil, errors.New("not implemented") 29 | } 30 | 31 | func TestSSAGen(t *testing.T) { 32 | stream := tokens.NewStream("main.bsp", code) 33 | tok := tokens.NewTokenizer(stream) 34 | err := tok.Tokenize() 35 | if err != nil { 36 | t.Fatal(err) 37 | } 38 | parse := parser.NewParser(tok) 39 | err = parse.Parse() 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | ir := ir.NewBuilder() 44 | err = ir.Build(parse, &fs{}) 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | 49 | // Actually generate 50 | i := ir.IR() 51 | gen := NewSSAGen(i) 52 | gen.Build() 53 | s := gen.SSA() 54 | 55 | fmt.Println("BEFORE:") 56 | fmt.Println(s) 57 | 58 | p := pipeline.New() 59 | p.ConstantPropagation() 60 | p.DeadCodeElimination() 61 | p.Run(s) 62 | 63 | fmt.Println("AFTER:") 64 | fmt.Println(s) 65 | 66 | rm := phirm.NewPhiRM(s) 67 | rm.Remove() 68 | fmt.Println("PHIRM:") 69 | fmt.Println(s) 70 | 71 | // Rebuild B# 72 | g := bspgen.NewBSPGen(s, i) 73 | out := g.Build() 74 | fmt.Println("B#:") 75 | spew.Dump(out.Body) 76 | } 77 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/src/test/completion.test.ts: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | * ------------------------------------------------------------------------------------------ */ 5 | 6 | import * as vscode from 'vscode'; 7 | import * as assert from 'assert'; 8 | import { getDocUri, activate } from './helper'; 9 | 10 | suite('Should do completion', () => { 11 | const docUri = getDocUri('completion.txt'); 12 | 13 | test('Completes JS/TS in txt file', async () => { 14 | await testCompletion(docUri, new vscode.Position(0, 0), { 15 | items: [ 16 | { label: 'JavaScript', kind: vscode.CompletionItemKind.Text }, 17 | { label: 'TypeScript', kind: vscode.CompletionItemKind.Text } 18 | ] 19 | }); 20 | }); 21 | }); 22 | 23 | async function testCompletion( 24 | docUri: vscode.Uri, 25 | position: vscode.Position, 26 | expectedCompletionList: vscode.CompletionList 27 | ) { 28 | await activate(docUri); 29 | 30 | // Executing the command `vscode.executeCompletionItemProvider` to simulate triggering completion 31 | const actualCompletionList = (await vscode.commands.executeCommand( 32 | 'vscode.executeCompletionItemProvider', 33 | docUri, 34 | position 35 | )) as vscode.CompletionList; 36 | 37 | assert.ok(actualCompletionList.items.length >= 2); 38 | expectedCompletionList.items.forEach((expectedItem, i) => { 39 | const actualItem = actualCompletionList.items[i]; 40 | assert.equal(actualItem.label, expectedItem.label); 41 | assert.equal(actualItem.kind, expectedItem.kind); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/out/test/diagnostics.test.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"diagnostics.test.js","sourceRoot":"","sources":["../../src/test/diagnostics.test.ts"],"names":[],"mappings":";AAAA;;;gGAGgG;;AAEhG,iCAAiC;AACjC,iCAAiC;AACjC,qCAA+C;AAE/C,KAAK,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACpC,MAAM,MAAM,GAAG,IAAA,kBAAS,EAAC,iBAAiB,CAAC,CAAC;IAE5C,IAAI,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,eAAe,CAAC,MAAM,EAAE;YAC7B,EAAE,OAAO,EAAE,uBAAuB,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;YAC3H,EAAE,OAAO,EAAE,uBAAuB,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;YAC7H,EAAE,OAAO,EAAE,sBAAsB,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;SAC5H,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,SAAS,OAAO,CAAC,KAAa,EAAE,KAAa,EAAE,KAAa,EAAE,KAAa;IAC1E,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC9C,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACrC,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,MAAkB,EAAE,mBAAwC;IAC1F,MAAM,IAAA,iBAAQ,EAAC,MAAM,CAAC,CAAC;IAEvB,MAAM,iBAAiB,GAAG,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAElE,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAEnE,mBAAmB,CAAC,OAAO,CAAC,CAAC,kBAAkB,EAAE,CAAC,EAAE,EAAE;QACrD,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACnE,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACnE,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;AACJ,CAAC"} -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/out/test/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.run = void 0; 4 | /* -------------------------------------------------------------------------------------------- 5 | * Copyright (c) Microsoft Corporation. All rights reserved. 6 | * Licensed under the MIT License. See License.txt in the project root for license information. 7 | * ------------------------------------------------------------------------------------------ */ 8 | const path = require("path"); 9 | const Mocha = require("mocha"); 10 | const glob = require("glob"); 11 | function run() { 12 | // Create the mocha test 13 | const mocha = new Mocha({ 14 | ui: 'tdd', 15 | color: true 16 | }); 17 | mocha.timeout(100000); 18 | const testsRoot = __dirname; 19 | return new Promise((resolve, reject) => { 20 | glob('**.test.js', { cwd: testsRoot }, (err, files) => { 21 | if (err) { 22 | return reject(err); 23 | } 24 | // Add files to the test suite 25 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 26 | try { 27 | // Run the mocha test 28 | mocha.run(failures => { 29 | if (failures > 0) { 30 | reject(new Error(`${failures} tests failed.`)); 31 | } 32 | else { 33 | resolve(); 34 | } 35 | }); 36 | } 37 | catch (err) { 38 | console.error(err); 39 | reject(err); 40 | } 41 | }); 42 | }); 43 | } 44 | exports.run = run; 45 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/src/test/helper.ts: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | * ------------------------------------------------------------------------------------------ */ 5 | 6 | import * as vscode from 'vscode'; 7 | import * as path from 'path'; 8 | 9 | export let doc: vscode.TextDocument; 10 | export let editor: vscode.TextEditor; 11 | export let documentEol: string; 12 | export let platformEol: string; 13 | 14 | /** 15 | * Activates the vscode.lsp-sample extension 16 | */ 17 | export async function activate(docUri: vscode.Uri) { 18 | // The extensionId is `publisher.name` from package.json 19 | const ext = vscode.extensions.getExtension('vscode-samples.lsp-sample')!; 20 | await ext.activate(); 21 | try { 22 | doc = await vscode.workspace.openTextDocument(docUri); 23 | editor = await vscode.window.showTextDocument(doc); 24 | await sleep(2000); // Wait for server activation 25 | } catch (e) { 26 | console.error(e); 27 | } 28 | } 29 | 30 | async function sleep(ms: number) { 31 | return new Promise(resolve => setTimeout(resolve, ms)); 32 | } 33 | 34 | export const getDocPath = (p: string) => { 35 | return path.resolve(__dirname, '../../testFixture', p); 36 | }; 37 | export const getDocUri = (p: string) => { 38 | return vscode.Uri.file(getDocPath(p)); 39 | }; 40 | 41 | export async function setTestContent(content: string): Promise { 42 | const all = new vscode.Range( 43 | doc.positionAt(0), 44 | doc.positionAt(doc.getText().length) 45 | ); 46 | return editor.edit(eb => eb.replace(all, content)); 47 | } 48 | -------------------------------------------------------------------------------- /backends/cgen/struct.go: -------------------------------------------------------------------------------- 1 | package cgen 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Nv7-Github/bsharp/ir" 7 | "github.com/Nv7-Github/bsharp/types" 8 | ) 9 | 10 | func (c *CGen) addGetStruct(n *ir.GetStructNode) (*Code, error) { 11 | s, err := c.AddNode(n.Struct) 12 | if err != nil { 13 | return nil, err 14 | } 15 | return &Code{ 16 | Value: fmt.Sprintf("%s->f%d", s.Value, n.Field), 17 | }, nil 18 | } 19 | 20 | func (c *CGen) addSetStruct(n *ir.SetStructNode) (*Code, error) { 21 | s, err := c.AddNode(n.Struct) 22 | if err != nil { 23 | return nil, err 24 | } 25 | v, err := c.AddNode(n.Value) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | pre := v.Pre 31 | free := "" 32 | if isDynamic(n.Value.Type()) { 33 | // Need to grab value 34 | pre = JoinCode(pre, c.GrabCode(v.Value, n.Value.Type())) 35 | 36 | // Then, save old value, free after the new value is set (because new value might rely on old value) 37 | old := c.GetTmp("old") 38 | pre = JoinCode(s.Pre, fmt.Sprintf("%s %s = %s->f%d;", c.CType(n.Value.Type()), old, s.Value, n.Field), pre) 39 | free = c.FreeCode(old, n.Value.Type()) 40 | } else { 41 | pre = JoinCode(s.Pre, pre) 42 | } 43 | 44 | // Set value 45 | code := fmt.Sprintf("%s->f%d = %s;", s.Value, n.Field, v.Value) 46 | return &Code{ 47 | Pre: JoinCode(pre, code, free), 48 | }, nil 49 | } 50 | 51 | func (c *CGen) ZeroValue(t types.Type) string { 52 | switch t.BasicType() { 53 | case types.INT, types.FLOAT, types.BYTE: 54 | return "0" 55 | 56 | case types.BOOL: 57 | return "false" 58 | 59 | case types.STRING, types.ARRAY, types.MAP, types.FUNCTION, types.STRUCT, types.ANY: 60 | return "NULL" 61 | } 62 | 63 | panic("invalid type") // might be NULL, but NULL should never be in a struct 64 | } 65 | -------------------------------------------------------------------------------- /bsharp_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "errors" 6 | "io" 7 | "path/filepath" 8 | "testing" 9 | 10 | "github.com/Nv7-Github/bsharp/ir" 11 | "github.com/Nv7-Github/bsharp/parser" 12 | "github.com/Nv7-Github/bsharp/tokens" 13 | ) 14 | 15 | //go:embed std/*.bsp examples/*.bsp 16 | var fuzzcorpus embed.FS 17 | 18 | type FuzzFS struct{} 19 | 20 | func (f *FuzzFS) Parse(name string) (*parser.Parser, error) { 21 | return nil, errors.New("not implemented") 22 | } 23 | 24 | func FuzzIR(f *testing.F) { 25 | files, err := fuzzcorpus.ReadDir("std") 26 | if err != nil { 27 | f.Fatal(err) 28 | } 29 | for _, fi := range files { 30 | v, err := fuzzcorpus.Open(filepath.Join("std", fi.Name())) 31 | if err != nil { 32 | f.Fatal(err) 33 | } 34 | c, err := io.ReadAll(v) 35 | if err != nil { 36 | f.Fatal(err) 37 | } 38 | f.Add(string(c)) 39 | v.Close() 40 | } 41 | files, err = fuzzcorpus.ReadDir("examples") 42 | if err != nil { 43 | f.Fatal(err) 44 | } 45 | for _, fi := range files { 46 | v, err := fuzzcorpus.Open(filepath.Join("examples", fi.Name())) 47 | if err != nil { 48 | f.Fatal(err) 49 | } 50 | c, err := io.ReadAll(v) 51 | if err != nil { 52 | f.Fatal(err) 53 | } 54 | f.Add(string(c)) 55 | v.Close() 56 | } 57 | 58 | f.Fuzz(func(t *testing.T, code string) { 59 | s := tokens.NewStream("main.bsp", code) 60 | tok := tokens.NewTokenizer(s) 61 | err := tok.Tokenize() 62 | if err != nil { 63 | t.Skip("can't tokenize") 64 | } 65 | 66 | p := parser.NewParser(tok) 67 | err = p.Parse() 68 | if err != nil { 69 | t.Skip("can't parse") 70 | } 71 | 72 | bld := ir.NewBuilder() 73 | err = bld.Build(p, &FuzzFS{}) 74 | if err != nil { 75 | t.Skip("can't build") 76 | } 77 | }) 78 | } 79 | -------------------------------------------------------------------------------- /bsp/ir/scope.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "bsp/types.bsp"] 2 | [IMPORT "bsp/tokens.bsp"] 3 | [TYPEDEF scopeType INT] 4 | [CONSTDEF scopeTypeGlobal 0] 5 | [CONSTDEF scopeTypeFunction 1] 6 | [CONSTDEF scopeTypeIf 2] 7 | [CONSTDEF scopeTypeWhile 3] 8 | [CONSTDEF scopeTypeSwitch 4] 9 | [CONSTDEF scopeTypeCase 5] 10 | 11 | [TYPEDEF variable STRUCT{typ:type, name:STRING, id:INT, pos:pos, scope:scopeType, needsGlobal:BOOL}] 12 | [TYPEDEF scopeFrame STRUCT{typ:scopeType, variables:MAP{STRING, INT}}] 13 | [TYPEDEF scope STRUCT{frames:ARRAY{scopeFrame}, variables:ARRAY{variable}}] 14 | [TYPEDEF scopeInfo ARRAY{scopeFrame}] 15 | 16 | [FUNC scopePush [PARAM scope scope] [PARAM typ scopeType] 17 | [DEFINE frame [MAKE scopeFrame]] 18 | [SET [VAR frame] variables [MAKE MAP{STRING, INT}]] 19 | [SET [VAR frame] typ [VAR typ]] 20 | [APPEND [GET [VAR scope] frames] [VAR frame]] 21 | ] 22 | 23 | [FUNC scopePop [PARAM scope scope] 24 | [DEFINE frames [GET [VAR scope] frames]] 25 | [SLICE [VAR frames] 0 [MATH [LENGTH [VAR frames]] - 1]] 26 | ] 27 | 28 | [FUNC scopeContainsType [PARAM scope scope] [PARAM typ scopeType] [RETURNS BOOL] 29 | [DEFINE frames [GET [VAR scope] frames]] 30 | [DEFINE i 0] 31 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR frames]]] 32 | [IF [COMPARE [GET [INDEX [VAR frames] [VAR i]] typ] == [VAR typ]] 33 | [RETURN TRUE] 34 | ] 35 | ] 36 | [RETURN FALSE] 37 | ] 38 | 39 | [FUNC scopeCurrType [PARAM scope scope] [RETURNS scopeType] 40 | [DEFINE f [INDEX [GET [VAR scope] frames] [MATH [LENGTH [GET [VAR scope] frames]] - 1]]] 41 | [RETURN [GET [VAR f] typ]] 42 | ] 43 | 44 | [FUNC scopeVariable [PARAM scope scope] [PARAM var INT] [RETURNS variable] 45 | [RETURN [INDEX [GET [VAR scope] variables] [VAR var]]] 46 | ] 47 | 48 | # TODO: currScopeInfo, getVar, currScopeGetVar, addVariable -------------------------------------------------------------------------------- /old/ssa/constrm/cast.go: -------------------------------------------------------------------------------- 1 | package constrm 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/Nv7-Github/bsharp/old/ssa" 7 | "github.com/Nv7-Github/bsharp/types" 8 | ) 9 | 10 | func evalCast(blk *ssa.Block, i *ssa.Cast) *ssa.Const { 11 | if types.ANY.Equal(i.From) || types.ANY.Equal(i.To) { 12 | return nil 13 | } 14 | 15 | switch i.From.BasicType() { 16 | case types.INT: 17 | switch i.To.BasicType() { 18 | case types.FLOAT: 19 | return &ssa.Const{Typ: types.FLOAT, Value: float64(cnst(blk, i.Value).(int))} 20 | 21 | case types.STRING: 22 | return &ssa.Const{Typ: types.STRING, Value: strconv.Itoa(cnst(blk, i.Value).(int))} 23 | } 24 | fallthrough 25 | 26 | case types.FLOAT: 27 | switch i.To.BasicType() { 28 | case types.INT: 29 | return &ssa.Const{Typ: types.INT, Value: int(cnst(blk, i.Value).(float64))} 30 | 31 | case types.STRING: 32 | return &ssa.Const{Typ: types.STRING, Value: strconv.FormatFloat(cnst(blk, i.Value).(float64), 'f', -1, 64)} 33 | } 34 | fallthrough 35 | 36 | case types.STRING: 37 | switch i.To.BasicType() { 38 | case types.INT: 39 | val, err := strconv.Atoi(cnst(blk, i.Value).(string)) 40 | if err != nil { 41 | return nil 42 | } 43 | return &ssa.Const{Typ: types.INT, Value: val} 44 | 45 | case types.FLOAT: 46 | val, err := strconv.ParseFloat(cnst(blk, i.Value).(string), 64) 47 | if err != nil { 48 | return nil 49 | } 50 | return &ssa.Const{Typ: types.FLOAT, Value: val} 51 | } 52 | fallthrough 53 | 54 | case types.BOOL: 55 | switch i.To.BasicType() { 56 | case types.STRING: 57 | val := "false" 58 | if cnst(blk, i.Value).(bool) { 59 | val = "true" 60 | } 61 | return &ssa.Const{Typ: types.STRING, Value: val} 62 | } 63 | fallthrough 64 | 65 | default: 66 | return nil 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ir/import.go: -------------------------------------------------------------------------------- 1 | package ir 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/parser" 5 | "github.com/Nv7-Github/bsharp/std" 6 | "github.com/Nv7-Github/bsharp/tokens" 7 | ) 8 | 9 | type FS interface { 10 | Parse(src string) (*parser.Parser, error) 11 | } 12 | 13 | func (b *Builder) importPass(p *parser.Parser, fs FS) error { 14 | for _, node := range p.Nodes { 15 | call, ok := node.(*parser.CallNode) 16 | if !ok { 17 | continue 18 | } 19 | if call.Name != "IMPORT" { 20 | continue 21 | } 22 | 23 | // Its an import! 24 | if len(call.Args) != 1 { 25 | b.Error(ErrorLevelError, call.Pos(), "expect 1 argument to IMPORT") 26 | continue 27 | } 28 | 29 | // Get name 30 | nm := call.Args[0] 31 | nameV, ok := nm.(*parser.StringNode) 32 | if !ok { 33 | b.Error(ErrorLevelError, nm.Pos(), "expected import name") 34 | continue 35 | } 36 | name := nameV.Value 37 | 38 | // Check if imported 39 | _, exists := b.imported[name] 40 | if exists { 41 | continue 42 | } 43 | 44 | // Get file 45 | var p *parser.Parser 46 | _, exists = std.Std[name] 47 | if exists { 48 | // Parse 49 | stream := tokens.NewStream(name, std.Std[name]) 50 | tok := tokens.NewTokenizer(stream) 51 | err := tok.Tokenize() 52 | if err != nil { 53 | b.Error(ErrorLevelError, call.Pos(), "%s", err.Error()) 54 | continue 55 | } 56 | p = parser.NewParser(tok) 57 | err = p.Parse() 58 | if err != nil { 59 | return err 60 | } 61 | } else { 62 | var err error 63 | p, err = fs.Parse(name) 64 | if err != nil { 65 | b.Error(ErrorLevelError, call.Pos(), "%s", err.Error()) 66 | continue 67 | } 68 | } 69 | 70 | // Build file 71 | err := b.Build(p, fs) 72 | if err != nil { 73 | return err 74 | } 75 | } 76 | 77 | return nil 78 | } 79 | -------------------------------------------------------------------------------- /ir/const.go: -------------------------------------------------------------------------------- 1 | package ir 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | 7 | "github.com/Nv7-Github/bsharp/parser" 8 | "github.com/Nv7-Github/bsharp/tokens" 9 | "github.com/Nv7-Github/bsharp/types" 10 | ) 11 | 12 | type Const struct { 13 | typ types.Type 14 | pos *tokens.Pos 15 | 16 | Value any 17 | } 18 | 19 | func (c *Const) Type() types.Type { return c.typ } 20 | func (c *Const) Pos() *tokens.Pos { return c.pos } 21 | 22 | func (b *Builder) buildString(n *parser.StringNode) Node { 23 | return &Const{ 24 | typ: types.STRING, 25 | pos: n.Pos(), 26 | Value: n.Value, 27 | } 28 | } 29 | 30 | func (b *Builder) buildByte(n *parser.ByteNode) Node { 31 | return &Const{ 32 | typ: types.BYTE, 33 | pos: n.Pos(), 34 | Value: n.Value, 35 | } 36 | } 37 | 38 | func (b *Builder) buildIdent(n *parser.IdentNode) Node { 39 | return &Const{ 40 | typ: types.IDENT, 41 | pos: n.Pos(), 42 | Value: n.Value, 43 | } 44 | } 45 | 46 | func (b *Builder) buildBool(n *parser.BoolNode) Node { 47 | return &Const{ 48 | typ: types.BOOL, 49 | pos: n.Pos(), 50 | Value: n.Value, 51 | } 52 | } 53 | 54 | func NewConst(typ types.Type, pos *tokens.Pos, val any) *Const { 55 | return &Const{ 56 | typ: typ, 57 | pos: pos, 58 | Value: val, 59 | } 60 | } 61 | 62 | func (b *Builder) buildNumber(n *parser.NumberNode) (Node, error) { 63 | if strings.Contains(n.Value, ".") { 64 | v, err := strconv.ParseFloat(n.Value, 64) 65 | if err != nil { 66 | return nil, err 67 | } 68 | return &Const{ 69 | typ: types.FLOAT, 70 | pos: n.Pos(), 71 | Value: v, 72 | }, nil 73 | } 74 | v, err := strconv.ParseInt(n.Value, 10, 64) 75 | if err != nil { 76 | return nil, err 77 | } 78 | return &Const{ 79 | typ: types.INT, 80 | pos: n.Pos(), 81 | Value: int(v), 82 | }, nil 83 | } 84 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/out/test/completion.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* -------------------------------------------------------------------------------------------- 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * Licensed under the MIT License. See License.txt in the project root for license information. 5 | * ------------------------------------------------------------------------------------------ */ 6 | Object.defineProperty(exports, "__esModule", { value: true }); 7 | const vscode = require("vscode"); 8 | const assert = require("assert"); 9 | const helper_1 = require("./helper"); 10 | suite('Should do completion', () => { 11 | const docUri = (0, helper_1.getDocUri)('completion.txt'); 12 | test('Completes JS/TS in txt file', async () => { 13 | await testCompletion(docUri, new vscode.Position(0, 0), { 14 | items: [ 15 | { label: 'JavaScript', kind: vscode.CompletionItemKind.Text }, 16 | { label: 'TypeScript', kind: vscode.CompletionItemKind.Text } 17 | ] 18 | }); 19 | }); 20 | }); 21 | async function testCompletion(docUri, position, expectedCompletionList) { 22 | await (0, helper_1.activate)(docUri); 23 | // Executing the command `vscode.executeCompletionItemProvider` to simulate triggering completion 24 | const actualCompletionList = (await vscode.commands.executeCommand('vscode.executeCompletionItemProvider', docUri, position)); 25 | assert.ok(actualCompletionList.items.length >= 2); 26 | expectedCompletionList.items.forEach((expectedItem, i) => { 27 | const actualItem = actualCompletionList.items[i]; 28 | assert.equal(actualItem.label, expectedItem.label); 29 | assert.equal(actualItem.kind, expectedItem.kind); 30 | }); 31 | } 32 | //# sourceMappingURL=completion.test.js.map -------------------------------------------------------------------------------- /old/ssa/val.go: -------------------------------------------------------------------------------- 1 | package ssa 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Nv7-Github/bsharp/ir" 7 | "github.com/Nv7-Github/bsharp/types" 8 | ) 9 | 10 | type Const struct { 11 | Value interface{} 12 | Typ types.Type 13 | } 14 | 15 | func (c *Const) Type() types.Type { return c.Typ } 16 | func (c *Const) String() string { 17 | return fmt.Sprintf("Const {%s}(%v)", c.Type().String(), c.Value) 18 | } 19 | func (c *Const) Args() []ID { return []ID{} } 20 | func (c *Const) SetArgs(_ []ID) {} 21 | 22 | type Compare struct { 23 | Op ir.CompareOperation 24 | Lhs ID 25 | Rhs ID 26 | Typ types.Type 27 | } 28 | 29 | func (c *Compare) Type() types.Type { return c.Typ } 30 | func (c *Compare) String() string { 31 | return fmt.Sprintf("Compare (%s) %s (%s)", c.Lhs.String(), c.Op.String(), c.Rhs.String()) 32 | } 33 | func (c *Compare) Args() []ID { return []ID{c.Lhs, c.Rhs} } 34 | func (c *Compare) SetArgs(v []ID) { 35 | c.Lhs = v[0] 36 | c.Rhs = v[1] 37 | } 38 | 39 | type Math struct { 40 | Op ir.MathOperation 41 | Lhs ID 42 | Rhs ID 43 | Typ types.Type 44 | } 45 | 46 | func (m *Math) Type() types.Type { return m.Typ } 47 | func (m *Math) String() string { 48 | return fmt.Sprintf("Math (%s) %s (%s)", m.Lhs.String(), m.Op.String(), m.Rhs.String()) 49 | } 50 | func (m *Math) Args() []ID { return []ID{m.Lhs, m.Rhs} } 51 | func (m *Math) SetArgs(v []ID) { 52 | m.Lhs = v[0] 53 | m.Rhs = v[1] 54 | } 55 | 56 | type Cast struct { 57 | Value ID 58 | From types.Type 59 | To types.Type 60 | } 61 | 62 | func (c *Cast) Type() types.Type { return c.To } 63 | func (c *Cast) String() string { 64 | return fmt.Sprintf("Cast (%s)[%s -> %s]", c.Value.String(), c.From.String(), c.To.String()) 65 | } 66 | func (c *Cast) Args() []ID { return []ID{c.Value} } 67 | func (c *Cast) SetArgs(v []ID) { 68 | c.Value = v[0] 69 | } 70 | -------------------------------------------------------------------------------- /examples/main.bsp: -------------------------------------------------------------------------------- 1 | # Functions 2 | [IMPORT "import.bsp"] 3 | [IMPORT "math.bsp"] 4 | 5 | [FUNC HELLO 6 | [PRINT "Hello, World!"] 7 | ] 8 | 9 | [HELLO] 10 | [PRINT [STRING [ADD 1 2]]] 11 | 12 | # Some basic Functions 13 | [DEFINE a 1] 14 | [PRINT [STRING [VAR a]]] 15 | [PRINT [CONCAT "hello w" "orld"]] 16 | [IF [COMPARE 6 != 4] 17 | [PRINT "6 is not 4"] 18 | ELSE 19 | [PRINT "6 is 4, uh oh!"] 20 | ] 21 | [PRINT [STRING [INDEX "Hi!" 0]]] 22 | [PRINT [STRING [LENGTH "Hello, World!"]]] 23 | 24 | [DEFINE i 0] 25 | [WHILE [COMPARE [VAR i] < 100] 26 | [PRINT [STRING [VAR i]]] 27 | [DEFINE i [MATH [VAR i] + 1]] 28 | ] 29 | 30 | [PRINT [STRING [FLOAT 100]]] 31 | 32 | [PRINT [STRING [RANDINT 1 100]]] 33 | [PRINT [STRING [RANDOM 1.0 100.0]]] 34 | [PRINT [STRING [CEIL 1.5]]] 35 | [PRINT [STRING [FLOOR 1.5]]] 36 | [PRINT [STRING [ROUND 1.5]]] 37 | 38 | # Maps 39 | [DEFINE b [MAKE MAP{STRING,STRING}]] 40 | [SET [VAR b] "Hello" "World"] 41 | [PRINT [GET [VAR b] "Hello"]] 42 | 43 | # Arrays 44 | [DEFINE c [ARRAY 1 2 3]] 45 | [PRINT [STRING [INDEX [VAR c] 0]]] 46 | [APPEND [VAR c] 4] 47 | 48 | # Switch-case 49 | [SWITCH "Hello" 50 | [CASE "World" 51 | [PRINT "The value was world!"] 52 | ] 53 | 54 | [CASE "Hello" 55 | [PRINT "The value was hello!"] 56 | ] 57 | 58 | [DEFAULT 59 | [PRINT "Unknown value!"] 60 | ] 61 | ] 62 | 63 | # First-class Functions 64 | [HELLO] # Call function with alias 65 | [CALL [FN HELLO] ""] # Call function without alias 66 | [DEFINE fns [MAKE MAP{STRING,FUNC{}NIL}]] # Make a dictionary of functions 67 | [SET [VAR fns] "hello" [FN HELLO]] 68 | [CALL [GET [VAR fns] "hello"] ""] # Call function from dictionary 69 | 70 | # Structs 71 | [DEFINE s [MAKE STRUCT{a:INT, b: INT}]] 72 | [SET [VAR s] a 0] 73 | [SET [VAR s] b 1] 74 | [PRINT [STRING [GET [VAR s] a]]] 75 | [PRINT [STRING [GET [VAR s] b]]] 76 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bsharp", 3 | "description": "The B# programming language!", 4 | "author": "Microsoft Corporation", 5 | "license": "MIT", 6 | "version": "1.1.1", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/b-Development-Team/bsharp" 10 | }, 11 | "publisher": "Nv7", 12 | "categories": [], 13 | "keywords": [ 14 | "multi-root ready" 15 | ], 16 | "engines": { 17 | "vscode": "^1.63.0" 18 | }, 19 | "activationEvents": [ 20 | "onLanguage:bsharp" 21 | ], 22 | "main": "./client/out/extension", 23 | "contributes": { 24 | "languages": [ 25 | { 26 | "id": "bsharp", 27 | "aliases": [ 28 | "B#", 29 | "bsharp" 30 | ], 31 | "extensions": [ 32 | "bsp", 33 | "bsharp" 34 | ], 35 | "configuration": "./language-configuration.json" 36 | } 37 | ], 38 | "grammars": [ 39 | { 40 | "language": "bsharp", 41 | "scopeName": "source.bsharp", 42 | "path": "./language-highlight.json" 43 | } 44 | ], 45 | "configuration": { 46 | "type": "object", 47 | "title": "B#", 48 | "properties": { 49 | "discordSupport": { 50 | "scope": "resource", 51 | "type": "boolean", 52 | "default": false, 53 | "description": "Whether to include the discord extension." 54 | } 55 | } 56 | } 57 | }, 58 | "scripts": { 59 | "vscode:prepublish": "npm run compile", 60 | "compile": "tsc -b", 61 | "watch": "tsc -b -w", 62 | "lint": "eslint ./client/src --ext .ts,.tsx", 63 | "postinstall": "cd client && npm install && cd .." 64 | }, 65 | "devDependencies": { 66 | "@types/mocha": "^9.1.0", 67 | "@types/node": "^14.17.0", 68 | "@typescript-eslint/eslint-plugin": "^5.13.0", 69 | "@typescript-eslint/parser": "^5.13.0", 70 | "eslint": "^8.10.0", 71 | "mocha": "^9.2.1", 72 | "typescript": "^4.6.2" 73 | } 74 | } -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/out/extension.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* -------------------------------------------------------------------------------------------- 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * Licensed under the MIT License. See License.txt in the project root for license information. 5 | * ------------------------------------------------------------------------------------------ */ 6 | Object.defineProperty(exports, "__esModule", { value: true }); 7 | exports.deactivate = exports.activate = void 0; 8 | const cp = require("child_process"); 9 | const node_1 = require("vscode-languageclient/node"); 10 | let client; 11 | async function activate(context) { 12 | // If the extension is launched in debug mode then the debug server options are used 13 | // Otherwise the run options are used 14 | // Get GOPATH 15 | let gopath = cp.execSync('go env GOPATH').toString(); 16 | console.log(gopath); 17 | const serverOptions = { 18 | command: gopath.trim() + "/bin/bsharp-lsp", 19 | args: [], 20 | transport: node_1.TransportKind.stdio, // also tried every other option 21 | }; 22 | // Options to control the language client 23 | const clientOptions = { 24 | // Register the server for plain text documents 25 | documentSelector: [{ scheme: "file", "language": "bsharp" }] 26 | }; 27 | // Create the language client and start the client. 28 | client = new node_1.LanguageClient('bsharp', 'B# Language Server', serverOptions, clientOptions); 29 | // Start the client. This will also launch the server 30 | client.start(); 31 | } 32 | exports.activate = activate; 33 | function deactivate() { 34 | if (!client) { 35 | return undefined; 36 | } 37 | return client.stop(); 38 | } 39 | exports.deactivate = deactivate; 40 | //# sourceMappingURL=extension.js.map -------------------------------------------------------------------------------- /backends/cgen/fns.go: -------------------------------------------------------------------------------- 1 | package cgen 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/Nv7-Github/bsharp/ir" 8 | "github.com/Nv7-Github/bsharp/types" 9 | ) 10 | 11 | func (c *CGen) addFnCall(n *ir.FnCallNode) (*Code, error) { 12 | fn, err := c.AddNode(n.Fn) 13 | if err != nil { 14 | return nil, err 15 | } 16 | pre := fn.Pre 17 | 18 | call := &strings.Builder{} 19 | fmt.Fprintf(call, "%s(", fn.Value) 20 | for i, arg := range n.Params { 21 | if i != 0 { 22 | call.WriteString(", ") 23 | } 24 | arg, err := c.AddNode(arg) 25 | if err != nil { 26 | return nil, err 27 | } 28 | call.WriteString(arg.Value) 29 | pre = JoinCode(pre, arg.Pre) 30 | } 31 | call.WriteString(")") 32 | 33 | if isDynamic(n.Type()) { // Ret type is dynamic, free 34 | name := c.GetTmp("call") 35 | pre = JoinCode(pre, fmt.Sprintf("%s %s = %s;", c.CType(n.Type()), name, call.String())) 36 | c.stack.Add(c.FreeCode(name, n.Type())) 37 | 38 | return &Code{ 39 | Pre: pre, 40 | Value: name, 41 | }, nil 42 | } 43 | 44 | return &Code{ 45 | Pre: pre, 46 | Value: call.String(), 47 | }, nil 48 | } 49 | 50 | func (c *CGen) addFn(n *ir.FnNode) *Code { 51 | return &Code{ 52 | Value: fmt.Sprintf("(&%s)", Namespace+n.Name), 53 | } 54 | } 55 | 56 | func (c *CGen) addReturn(n *ir.ReturnNode) (*Code, error) { 57 | v, err := c.AddNode(n.Value) 58 | if err != nil { 59 | return nil, err 60 | } 61 | c.isReturn = true 62 | // Check if null return 63 | if types.NULL.Equal(n.Value.Type()) { 64 | return &Code{Pre: JoinCode(v.Pre, c.stack.FnFreeCode(), "return;")}, nil 65 | } 66 | // Grab if dynamic 67 | pre := JoinCode(c.stack.FnFreeCode(), fmt.Sprintf("return %s;", v.Value)) 68 | if isDynamic(n.Value.Type()) { 69 | pre = JoinCode(c.GrabCode(v.Value, n.Value.Type()), pre) 70 | } 71 | return &Code{ 72 | Pre: JoinCode(v.Pre, pre), 73 | }, nil 74 | } 75 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/language-highlight.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "scopeName": "source.bsharp", 4 | "patterns": [{ "include": "#node" }, { "include": "#comment" }], 5 | "repository": { 6 | "value": { 7 | "patterns": [{ "include": "#node" }, { "include": "#comment" }, { "include": "#string" }, { "include": "#byte" }, { "include": "#number" }, { "include": "#keyword" }, { "include": "#ident" }] 8 | }, 9 | "ident": { 10 | "match": "(?:\\p{L}|\\{|\\}|\\+|-|\\*|\\/|%|==|!=|<=|>=|<|>|\\^|,|:)+", 11 | "name": "token.ident.bsharp", 12 | "patterns": [{ "include": "#keyword"}] 13 | }, 14 | "keyword": { 15 | "match": "ELSE", 16 | "name": "keyword.function.bsharp" 17 | }, 18 | "number": { 19 | "match": "(?:-)?(?:[0-9]|\\.)+", 20 | "name": "constant.numeric.bsharp" 21 | }, 22 | "string": { 23 | "begin": "\"", 24 | "end": "\"", 25 | "name": "string.bsharp", 26 | "patterns": [ 27 | { 28 | "include": "#string_escaped_char" 29 | } 30 | ] 31 | }, 32 | "byte": { 33 | "begin": "'", 34 | "end": "'", 35 | "name": "string.quoted.single.bsharp", 36 | "patterns": [ 37 | { 38 | "include": "#string_escaped_char" 39 | } 40 | ] 41 | }, 42 | "string_escaped_char": { 43 | "match": "\\\\[nt\"\\\\']", 44 | "name": "constant.character.escape" 45 | }, 46 | "comment": { 47 | "begin": "#", 48 | "end": "#|\\n", 49 | "name": "comment.bsharp" 50 | }, 51 | "node": { 52 | "begin": "\\[([^ \\t\\n\\]]+)", 53 | "end": "\\]", 54 | "captures": { 55 | "1": { 56 | "name": "keyword.function.bsharp" 57 | } 58 | }, 59 | "name": "expression.tag.bsharp", 60 | "patterns": [{ "include": "#value" }] 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /parser/parse.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/tokens" 5 | ) 6 | 7 | func (p *Parser) ParseNode() (Node, error) { 8 | // Check if not call node 9 | switch p.t.Tok().Typ { 10 | case tokens.TokenTypeString: 11 | t := p.t.Tok() 12 | p.t.Eat() 13 | return &StringNode{ 14 | pos: t.Pos, 15 | Value: t.Value, 16 | }, nil 17 | 18 | case tokens.TokenTypeByte: 19 | t := p.t.Tok() 20 | p.t.Eat() 21 | return &ByteNode{ 22 | pos: t.Pos, 23 | Value: t.Value[0], 24 | }, nil 25 | 26 | case tokens.TokenTypeIdent: 27 | t := p.t.Tok() 28 | p.t.Eat() 29 | if t.Value == "TRUE" || t.Value == "FALSE" { 30 | return &BoolNode{ 31 | pos: t.Pos, 32 | Value: t.Value == "TRUE", 33 | }, nil 34 | } 35 | if t.Value == "NULL" { 36 | return &NullNode{ 37 | pos: t.Pos, 38 | }, nil 39 | } 40 | return &IdentNode{ 41 | pos: t.Pos, 42 | Value: t.Value, 43 | }, nil 44 | 45 | case tokens.TokenTypeNumber: 46 | t := p.t.Tok() 47 | p.t.Eat() 48 | return &NumberNode{ 49 | pos: t.Pos, 50 | Value: t.Value, 51 | }, nil 52 | } 53 | 54 | // LBrack 55 | pos := p.t.Tok().Pos 56 | if p.t.Tok().Typ != tokens.TokenTypeLBrack { 57 | return nil, p.t.Tok().Pos.Error("expected '['") 58 | } 59 | p.t.Eat() 60 | 61 | // Name 62 | if !p.t.HasNext() || p.t.Tok().Typ != tokens.TokenTypeIdent { 63 | return nil, p.t.Last().Error("expected identifier") 64 | } 65 | name := p.t.Tok().Value 66 | p.t.Eat() 67 | 68 | // Args 69 | args := make([]Node, 0) 70 | for p.t.HasNext() { 71 | if p.t.Tok().Typ == tokens.TokenTypeRBrack { // Eat ] 72 | pos = pos.Extend(p.t.Tok().Pos) 73 | p.t.Eat() 74 | break 75 | } 76 | 77 | arg, err := p.ParseNode() 78 | if err != nil { 79 | return nil, err 80 | } 81 | args = append(args, arg) 82 | } 83 | 84 | return &CallNode{ 85 | pos: pos, 86 | Name: name, 87 | Args: args, 88 | }, nil 89 | } 90 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/src/extension.ts: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | * ------------------------------------------------------------------------------------------ */ 5 | 6 | import * as path from 'path'; 7 | import { workspace, ExtensionContext } from 'vscode'; 8 | import cp = require('child_process'); 9 | 10 | import { 11 | LanguageClient, 12 | LanguageClientOptions, 13 | ServerOptions, 14 | TransportKind 15 | } from 'vscode-languageclient/node'; 16 | 17 | let client: LanguageClient; 18 | 19 | export async function activate(context: ExtensionContext) { 20 | // If the extension is launched in debug mode then the debug server options are used 21 | // Otherwise the run options are used 22 | 23 | // Get GOPATH 24 | let gopath = cp.execSync('go env GOPATH').toString(); 25 | console.log(gopath); 26 | const serverOptions: ServerOptions = { 27 | command: gopath.trim() + "/bin/bsharp-lsp", 28 | args: [], 29 | transport: TransportKind.stdio, // also tried every other option 30 | }; 31 | 32 | // Options to control the language client 33 | const clientOptions: LanguageClientOptions = { 34 | // Register the server for plain text documents 35 | documentSelector: [{scheme: "file", "language": "bsharp"}] 36 | }; 37 | 38 | // Create the language client and start the client. 39 | client = new LanguageClient( 40 | 'bsharp', 41 | 'B# Language Server', 42 | serverOptions, 43 | clientOptions 44 | ); 45 | 46 | 47 | // Start the client. This will also launch the server 48 | client.start(); 49 | } 50 | 51 | export function deactivate(): Thenable | undefined { 52 | if (!client) { 53 | return undefined; 54 | } 55 | return client.stop(); 56 | } 57 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/out/test/helper.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* -------------------------------------------------------------------------------------------- 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * Licensed under the MIT License. See License.txt in the project root for license information. 5 | * ------------------------------------------------------------------------------------------ */ 6 | Object.defineProperty(exports, "__esModule", { value: true }); 7 | exports.setTestContent = exports.getDocUri = exports.getDocPath = exports.activate = exports.platformEol = exports.documentEol = exports.editor = exports.doc = void 0; 8 | const vscode = require("vscode"); 9 | const path = require("path"); 10 | /** 11 | * Activates the vscode.lsp-sample extension 12 | */ 13 | async function activate(docUri) { 14 | // The extensionId is `publisher.name` from package.json 15 | const ext = vscode.extensions.getExtension('vscode-samples.lsp-sample'); 16 | await ext.activate(); 17 | try { 18 | exports.doc = await vscode.workspace.openTextDocument(docUri); 19 | exports.editor = await vscode.window.showTextDocument(exports.doc); 20 | await sleep(2000); // Wait for server activation 21 | } 22 | catch (e) { 23 | console.error(e); 24 | } 25 | } 26 | exports.activate = activate; 27 | async function sleep(ms) { 28 | return new Promise(resolve => setTimeout(resolve, ms)); 29 | } 30 | const getDocPath = (p) => { 31 | return path.resolve(__dirname, '../../testFixture', p); 32 | }; 33 | exports.getDocPath = getDocPath; 34 | const getDocUri = (p) => { 35 | return vscode.Uri.file((0, exports.getDocPath)(p)); 36 | }; 37 | exports.getDocUri = getDocUri; 38 | async function setTestContent(content) { 39 | const all = new vscode.Range(exports.doc.positionAt(0), exports.doc.positionAt(exports.doc.getText().length)); 40 | return exports.editor.edit(eb => eb.replace(all, content)); 41 | } 42 | exports.setTestContent = setTestContent; 43 | //# sourceMappingURL=helper.js.map -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/src/test/diagnostics.test.ts: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | * ------------------------------------------------------------------------------------------ */ 5 | 6 | import * as vscode from 'vscode'; 7 | import * as assert from 'assert'; 8 | import { getDocUri, activate } from './helper'; 9 | 10 | suite('Should get diagnostics', () => { 11 | const docUri = getDocUri('diagnostics.txt'); 12 | 13 | test('Diagnoses uppercase texts', async () => { 14 | await testDiagnostics(docUri, [ 15 | { message: 'ANY is all uppercase.', range: toRange(0, 0, 0, 3), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' }, 16 | { message: 'ANY is all uppercase.', range: toRange(0, 14, 0, 17), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' }, 17 | { message: 'OS is all uppercase.', range: toRange(0, 18, 0, 20), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' } 18 | ]); 19 | }); 20 | }); 21 | 22 | function toRange(sLine: number, sChar: number, eLine: number, eChar: number) { 23 | const start = new vscode.Position(sLine, sChar); 24 | const end = new vscode.Position(eLine, eChar); 25 | return new vscode.Range(start, end); 26 | } 27 | 28 | async function testDiagnostics(docUri: vscode.Uri, expectedDiagnostics: vscode.Diagnostic[]) { 29 | await activate(docUri); 30 | 31 | const actualDiagnostics = vscode.languages.getDiagnostics(docUri); 32 | 33 | assert.equal(actualDiagnostics.length, expectedDiagnostics.length); 34 | 35 | expectedDiagnostics.forEach((expectedDiagnostic, i) => { 36 | const actualDiagnostic = actualDiagnostics[i]; 37 | assert.equal(actualDiagnostic.message, expectedDiagnostic.message); 38 | assert.deepEqual(actualDiagnostic.range, expectedDiagnostic.range); 39 | assert.equal(actualDiagnostic.severity, expectedDiagnostic.severity); 40 | }); 41 | } -------------------------------------------------------------------------------- /backends/interpreter/block.go: -------------------------------------------------------------------------------- 1 | package interpreter 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | ) 6 | 7 | func (i *Interpreter) evalIfNode(n *ir.IfNode) error { 8 | cond, err := i.evalNode(n.Condition) 9 | if err != nil { 10 | return err 11 | } 12 | if cond.Value.(bool) { 13 | i.stack.Push() 14 | for _, node := range n.Body { 15 | if _, err := i.evalNode(node); err != nil { 16 | return err 17 | } 18 | } 19 | i.stack.Pop() 20 | } else if n.Else != nil { 21 | i.stack.Push() 22 | for _, node := range n.Else { 23 | if _, err := i.evalNode(node); err != nil { 24 | return err 25 | } 26 | } 27 | i.stack.Pop() 28 | } 29 | return nil 30 | } 31 | 32 | func (i *Interpreter) evalWhileNode(n *ir.WhileNode) error { 33 | for { 34 | if i.retVal != nil { 35 | return nil 36 | } 37 | cond, err := i.evalNode(n.Condition) 38 | if err != nil { 39 | return err 40 | } 41 | if !cond.Value.(bool) { 42 | break 43 | } 44 | 45 | i.stack.Push() 46 | for _, node := range n.Body { 47 | if _, err := i.evalNode(node); err != nil { 48 | return err 49 | } 50 | } 51 | i.stack.Pop() 52 | } 53 | return nil 54 | } 55 | 56 | func (i *Interpreter) evalSwitchNode(n *ir.SwitchNode) error { 57 | v, err := i.evalNode(n.Value) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | // Check cases (O(n), not O(1) like expected from switch) 63 | for _, cs := range n.Cases { 64 | if cs.Block.(*ir.Case).Value.Value == v.Value { // This works for int, float, string, all the hashable types 65 | i.stack.Push() 66 | for _, node := range cs.Block.(*ir.Case).Body { 67 | if _, err := i.evalNode(node); err != nil { 68 | return err 69 | } 70 | } 71 | i.stack.Pop() 72 | return nil 73 | } 74 | } 75 | 76 | // Default case 77 | if n.Default != nil { 78 | i.stack.Push() 79 | for _, node := range n.Default.Block.(*ir.Default).Body { 80 | if _, err := i.evalNode(node); err != nil { 81 | return err 82 | } 83 | } 84 | i.stack.Pop() 85 | } 86 | return nil 87 | } 88 | -------------------------------------------------------------------------------- /old/ssa/ir.go: -------------------------------------------------------------------------------- 1 | package ssa 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/Nv7-Github/bsharp/types" 7 | ) 8 | 9 | type IRNode int 10 | 11 | const ( 12 | IRNodeIndex IRNode = iota 13 | IRNodeLength 14 | IRNodeMake 15 | IRNodeGet 16 | IRNodeArray 17 | IRNodeFn 18 | IRNodeExists 19 | IRNodeKeys 20 | IRNodeTime 21 | IRNodeGetStruct 22 | IRNodeCanCast 23 | ) 24 | 25 | func (i IRNode) String() string { 26 | // Converts to string 27 | return [...]string{"Index", "Length", "Make", "Get", "Array", "Fn", "Exists", "Keys", "Time", "GetStruct", "CanCast"}[i] 28 | } 29 | 30 | type IRValue struct { 31 | Kind IRNode 32 | Params []ID 33 | Typ types.Type 34 | } 35 | 36 | func (i *IRValue) Type() types.Type { return i.Typ } 37 | func (i *IRValue) Args() []ID { return i.Params } 38 | func (i *IRValue) SetArgs(v []ID) { i.Params = v } 39 | func (i *IRValue) String() string { 40 | out := &strings.Builder{} 41 | out.WriteString(i.Kind.String()) 42 | out.WriteString(" (") 43 | for i, par := range i.Params { 44 | if i > 0 { 45 | out.WriteString(", ") 46 | } 47 | out.WriteString(par.String()) 48 | } 49 | out.WriteString(")") 50 | return out.String() 51 | } 52 | 53 | type LiveIRNode int 54 | 55 | const ( 56 | IRNodePrint LiveIRNode = iota 57 | IRNodeSet 58 | IRNodeAppend 59 | IRNodeSlice 60 | IRNodeSetIndex 61 | IRNodeSetStruct 62 | ) 63 | 64 | func (i LiveIRNode) String() string { 65 | return [...]string{"Print", "Set", "Append", "Slice", "SetIndex", "SetStruct"}[i] 66 | } 67 | 68 | type LiveIRValue struct { 69 | Kind LiveIRNode 70 | Params []ID 71 | Typ types.Type 72 | } 73 | 74 | func (i *LiveIRValue) Type() types.Type { return i.Typ } 75 | func (i *LiveIRValue) Args() []ID { return i.Params } 76 | func (i *LiveIRValue) SetArgs(v []ID) { i.Params = v } 77 | func (i *LiveIRValue) String() string { 78 | out := &strings.Builder{} 79 | out.WriteString(i.Kind.String()) 80 | out.WriteString(" (") 81 | for i, par := range i.Params { 82 | if i > 0 { 83 | out.WriteString(", ") 84 | } 85 | out.WriteString(par.String()) 86 | } 87 | out.WriteString(")") 88 | return out.String() 89 | } 90 | -------------------------------------------------------------------------------- /old/ssa/var.go: -------------------------------------------------------------------------------- 1 | package ssa 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Nv7-Github/bsharp/types" 7 | ) 8 | 9 | // Everything in this file is not real SSA, will be removed in a pass 10 | 11 | type SetVariable struct { 12 | Variable int 13 | Value ID 14 | } 15 | 16 | func (s *SetVariable) Type() types.Type { return types.NULL } 17 | func (s *SetVariable) String() string { 18 | return fmt.Sprintf("SetVariable (%s) -> [%d]", s.Value.String(), s.Variable) 19 | } 20 | func (s *SetVariable) Args() []ID { return []ID{s.Value} } 21 | func (s *SetVariable) SetArgs(v []ID) { 22 | s.Value = v[0] 23 | } 24 | 25 | type GetVariable struct { 26 | Variable int 27 | Typ types.Type 28 | } 29 | 30 | func (g *GetVariable) Type() types.Type { return g.Typ } 31 | func (g *GetVariable) String() string { 32 | return fmt.Sprintf("GetVariable [%d]", g.Variable) 33 | } 34 | func (g *GetVariable) Args() []ID { return []ID{} } 35 | func (g *GetVariable) SetArgs(_ []ID) {} 36 | 37 | type GlobalSetVariable struct { 38 | Variable int 39 | Value ID 40 | } 41 | 42 | func (s *GlobalSetVariable) Type() types.Type { return types.NULL } 43 | func (s *GlobalSetVariable) String() string { 44 | return fmt.Sprintf("GlobalSetVariable (%s) -> [%d]", s.Value.String(), s.Variable) 45 | } 46 | func (s *GlobalSetVariable) Args() []ID { return []ID{s.Value} } 47 | func (s *GlobalSetVariable) SetArgs(v []ID) { 48 | s.Value = v[0] 49 | } 50 | 51 | type GlobalGetVariable struct { 52 | Variable int 53 | Typ types.Type 54 | } 55 | 56 | func (g *GlobalGetVariable) Type() types.Type { return g.Typ } 57 | func (g *GlobalGetVariable) String() string { 58 | return fmt.Sprintf("GetVariable [%d]", g.Variable) 59 | } 60 | func (g *GlobalGetVariable) Args() []ID { return []ID{} } 61 | func (g *GlobalGetVariable) SetArgs(_ []ID) {} 62 | 63 | type GetParam struct { 64 | Variable int 65 | Typ types.Type 66 | } 67 | 68 | func (g *GetParam) Type() types.Type { return g.Typ } 69 | func (g *GetParam) String() string { 70 | return fmt.Sprintf("GetParam [%d]", g.Variable) 71 | } 72 | func (g *GetParam) Args() []ID { return []ID{} } 73 | func (g *GetParam) SetArgs(_ []ID) {} 74 | -------------------------------------------------------------------------------- /bsharp-lsp/vscode/client/out/test/diagnostics.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* -------------------------------------------------------------------------------------------- 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * Licensed under the MIT License. See License.txt in the project root for license information. 5 | * ------------------------------------------------------------------------------------------ */ 6 | Object.defineProperty(exports, "__esModule", { value: true }); 7 | const vscode = require("vscode"); 8 | const assert = require("assert"); 9 | const helper_1 = require("./helper"); 10 | suite('Should get diagnostics', () => { 11 | const docUri = (0, helper_1.getDocUri)('diagnostics.txt'); 12 | test('Diagnoses uppercase texts', async () => { 13 | await testDiagnostics(docUri, [ 14 | { message: 'ANY is all uppercase.', range: toRange(0, 0, 0, 3), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' }, 15 | { message: 'ANY is all uppercase.', range: toRange(0, 14, 0, 17), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' }, 16 | { message: 'OS is all uppercase.', range: toRange(0, 18, 0, 20), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' } 17 | ]); 18 | }); 19 | }); 20 | function toRange(sLine, sChar, eLine, eChar) { 21 | const start = new vscode.Position(sLine, sChar); 22 | const end = new vscode.Position(eLine, eChar); 23 | return new vscode.Range(start, end); 24 | } 25 | async function testDiagnostics(docUri, expectedDiagnostics) { 26 | await (0, helper_1.activate)(docUri); 27 | const actualDiagnostics = vscode.languages.getDiagnostics(docUri); 28 | assert.equal(actualDiagnostics.length, expectedDiagnostics.length); 29 | expectedDiagnostics.forEach((expectedDiagnostic, i) => { 30 | const actualDiagnostic = actualDiagnostics[i]; 31 | assert.equal(actualDiagnostic.message, expectedDiagnostic.message); 32 | assert.deepEqual(actualDiagnostic.range, expectedDiagnostic.range); 33 | assert.equal(actualDiagnostic.severity, expectedDiagnostic.severity); 34 | }); 35 | } 36 | //# sourceMappingURL=diagnostics.test.js.map -------------------------------------------------------------------------------- /tokens/stream.go: -------------------------------------------------------------------------------- 1 | package tokens 2 | 3 | import "fmt" 4 | 5 | type Pos struct { 6 | File string 7 | Line int 8 | Char int 9 | EndLine int 10 | EndChar int 11 | } 12 | 13 | type PosError struct { 14 | Msg string 15 | Pos *Pos 16 | } 17 | 18 | func (p *PosError) Error() string { 19 | return fmt.Sprintf("%s: %s", p.Pos.String(), p.Msg) 20 | } 21 | 22 | func (p *Pos) Error(format string, args ...any) error { 23 | return &PosError{ 24 | Msg: fmt.Sprintf(format, args...), 25 | Pos: p, 26 | } 27 | } 28 | 29 | func (a *Pos) Extend(b *Pos) *Pos { 30 | return &Pos{ 31 | File: a.File, 32 | Line: a.Line, 33 | Char: a.Char, 34 | EndLine: b.EndLine, 35 | EndChar: b.EndChar, 36 | } 37 | } 38 | 39 | func (p *Pos) Contains(b *Pos) bool { 40 | return p.File == b.File && ((b.Line == p.Line && b.Char > p.Char && b.Char < p.EndChar) || (p.Line < b.Line && b.Line < p.EndLine)) 41 | } 42 | 43 | func (p *Pos) String() string { 44 | return fmt.Sprintf("%s:%d:%d", p.File, p.Line+1, p.Char+1) 45 | } 46 | 47 | func (p *Pos) Dup() *Pos { 48 | return &Pos{ 49 | File: p.File, 50 | Line: p.Line, 51 | Char: p.Char, 52 | } 53 | } 54 | 55 | type Stream struct { 56 | code []rune 57 | pos int 58 | 59 | line int 60 | char int 61 | file string 62 | } 63 | 64 | func NewStream(file, code string) *Stream { 65 | return &Stream{ 66 | code: []rune(code), 67 | file: file, 68 | } 69 | } 70 | 71 | func (s *Stream) Char() rune { 72 | return s.code[s.pos] 73 | } 74 | 75 | func (s *Stream) CanPeek(off int) bool { 76 | return s.pos+off < len(s.code) 77 | } 78 | 79 | func (s *Stream) Peek(off int) rune { 80 | return s.code[s.pos+off] 81 | } 82 | 83 | func (s *Stream) Pos() *Pos { 84 | return &Pos{ 85 | File: s.file, 86 | Line: s.line, 87 | Char: s.char, 88 | 89 | EndLine: s.line, 90 | EndChar: s.char + 1, 91 | } 92 | } 93 | 94 | func (s *Stream) Eat() { 95 | if s.Char() == '\n' { 96 | s.line++ 97 | s.char = 0 98 | } else { 99 | s.char++ 100 | } 101 | s.pos++ 102 | } 103 | 104 | func (s *Stream) HasNext() bool { 105 | return s.pos < len(s.code) 106 | } 107 | -------------------------------------------------------------------------------- /old/ssa/constrm/constrm.go: -------------------------------------------------------------------------------- 1 | // Package constrm applies constant folding optimizations 2 | package constrm 3 | 4 | import ( 5 | "github.com/Nv7-Github/bsharp/old/ssa" 6 | ) 7 | 8 | func checkInstrConst(instr ssa.Instruction, block *ssa.Block) bool { 9 | for _, arg := range instr.Args() { 10 | _, ok := block.Instructions[arg].(*ssa.Const) 11 | if !ok { 12 | return false 13 | } 14 | } 15 | return true 16 | } 17 | 18 | func cnst(blk *ssa.Block, id ssa.ID) interface{} { 19 | return blk.Instructions[id].(*ssa.Const).Value 20 | } 21 | 22 | func globalcnst(s *ssa.SSA, id ssa.ID) *ssa.Const { 23 | blk := s.Blocks[s.Instructions[id].Block] 24 | return blk.Instructions[id].(*ssa.Const) 25 | } 26 | 27 | // Constrm initially converts all non-phi consts into constant values 28 | func Constrm(s *ssa.SSA) { 29 | todo := []string{s.EntryBlock} 30 | done := make(map[string]struct{}) 31 | for len(todo) > 0 { 32 | t := todo[0] 33 | blk := s.Blocks[t] 34 | todo = todo[1:] 35 | _, exists := done[blk.Label] 36 | if !exists { 37 | todo = append(todo, blk.After()...) 38 | done[blk.Label] = struct{}{} 39 | } 40 | 41 | // Constant folding 42 | for _, id := range blk.Order { 43 | instr := blk.Instructions[id] 44 | 45 | switch i := instr.(type) { 46 | case *ssa.Math: 47 | if checkInstrConst(instr, blk) { 48 | v := evalMath(blk, i) 49 | if v != nil { 50 | blk.Instructions[id] = v 51 | } 52 | } 53 | 54 | case *ssa.Compare: 55 | if checkInstrConst(instr, blk) { 56 | v := evalComp(blk, i) 57 | if v != nil { 58 | blk.Instructions[id] = v 59 | } 60 | } 61 | 62 | case *ssa.Cast: 63 | if checkInstrConst(instr, blk) { 64 | v := evalCast(blk, i) 65 | if v != nil { 66 | blk.Instructions[id] = v 67 | } 68 | } 69 | 70 | case *ssa.Concat: 71 | if checkInstrConst(instr, blk) { 72 | v := evalConcat(blk, i) 73 | if v != nil { 74 | blk.Instructions[id] = v 75 | } 76 | } 77 | 78 | case *ssa.LogicalOp: 79 | if checkInstrConst(instr, blk) { 80 | v := evalLogicalOp(blk, i) 81 | if v != nil { 82 | blk.Instructions[id] = v 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /bsharp-lsp/ir.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | "github.com/Nv7-Github/bsharp/tokens" 6 | ) 7 | 8 | func GetScope(i *ir.IR, pos *tokens.Pos) *ir.ScopeInfo { 9 | for _, fn := range i.Funcs { 10 | if fn.Pos().Contains(pos) { 11 | for _, node := range fn.Body { 12 | _, ok := node.(*ir.BlockNode) 13 | if ok && node.Pos().Contains(pos) { 14 | s := scopeRecurse(node.(*ir.BlockNode), pos) 15 | if s != nil { 16 | return s 17 | } 18 | } 19 | } 20 | return fn.Scope 21 | } 22 | } 23 | 24 | for _, node := range i.Body { 25 | _, ok := node.(*ir.BlockNode) 26 | if ok && node.Pos().Contains(pos) { 27 | s := scopeRecurse(node.(*ir.BlockNode), pos) 28 | if s != nil { 29 | return s 30 | } 31 | } 32 | } 33 | return i.GlobalScope 34 | } 35 | 36 | func scopeRecurse(bl *ir.BlockNode, pos *tokens.Pos) *ir.ScopeInfo { 37 | switch b := bl.Block.(type) { 38 | case ir.BodyBlock: 39 | for _, node := range b.Block() { 40 | _, ok := node.(*ir.BlockNode) 41 | if ok && node.Pos().Contains(pos) { 42 | s := scopeRecurse(node.(*ir.BlockNode), pos) 43 | if s != nil { 44 | return s 45 | } 46 | } 47 | } 48 | return b.ScopeInfo() 49 | 50 | case *ir.IfNode: 51 | if len(b.Else) > 0 && b.Else[0].Pos().Extend(b.Else[len(b.Else)-1].Pos()).Contains(pos) { // In else 52 | for _, node := range b.Else { 53 | _, ok := node.(*ir.BlockNode) 54 | if ok && node.Pos().Contains(pos) { 55 | s := scopeRecurse(node.(*ir.BlockNode), pos) 56 | if s != nil { 57 | return s 58 | } 59 | } 60 | } 61 | return b.ElseScope 62 | } 63 | 64 | for _, node := range b.Body { 65 | _, ok := node.(*ir.BlockNode) 66 | if ok && node.Pos().Contains(pos) { 67 | s := scopeRecurse(node.(*ir.BlockNode), pos) 68 | if s != nil { 69 | return s 70 | } 71 | } 72 | } 73 | return b.Scope 74 | 75 | case *ir.SwitchNode: 76 | for _, cs := range b.Cases { 77 | if cs.Pos().Contains(pos) { 78 | return scopeRecurse(cs, pos) 79 | } 80 | } 81 | 82 | if b.Default != nil && b.Default.Pos().Contains(pos) { 83 | return scopeRecurse(b.Default, pos) 84 | } 85 | } 86 | 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /bsp/main.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "bsp/tokens.bsp"] 2 | [IMPORT "bsp/parser.bsp"] 3 | [IMPORT "bsp/types.bsp"] 4 | [IMPORT "bsp/ir/ir.bsp"] 5 | [IMPORT "bsp/ir/build.bsp"] 6 | 7 | [DEFINE code "[PRINT \"Hello, World!\"]\n[MATH 1 + 1]\n[STRING 1]\n"] 8 | 9 | # Tokenize 10 | [DEFINE tokenserr [TOKENIZE "hello.bsp" [VAR code]]] 11 | [IF [CANCAST [VAR tokenserr] error] 12 | [FAIL [VAR tokenserr]] 13 | ] 14 | [DEFINE tokens [CAST [VAR tokenserr] ARRAY{token}]] 15 | 16 | # Print 17 | [PRINT "TOKENS: "] 18 | [DEFINE i 0] 19 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR tokens]]] 20 | [DEFINE tok [INDEX [VAR tokens] [VAR i]]] 21 | [PRINT [CONCAT "{" [POSSTR [GET [VAR tok] pos]] ", " [STRING [GET [VAR tok] typ]] ", " [GET [VAR tok] val] "}"]] 22 | [DEFINE i [MATH [VAR i] + 1]] 23 | ] 24 | 25 | # Parse 26 | [DEFINE perr [PARSE [VAR tokens]]] 27 | [IF [CANCAST [VAR perr] error] 28 | [FAIL [VAR perr]] 29 | ] 30 | [DEFINE p [CAST [VAR perr] ARRAY{parserNode}]] 31 | 32 | # Print 33 | [PRINT "\nNODES: "] 34 | [DEFINE i 0] 35 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR p]]] 36 | [PRINTNODE [INDEX [VAR p] [VAR i]] ""] 37 | [DEFINE i [MATH [VAR i] + 1]] 38 | ] 39 | 40 | # Types 41 | [PRINT "\nTYPES:"] 42 | [DEFINE a [_ARRAY [_INT]]] 43 | [DEFINE b [FUNCTION [ARRAY [_INT] [_STRING]] [_NULL]]] 44 | [DEFINE c [STRUCT [ARRAY [FIELD "h" [_INT]] [FIELD "i" [_STRING]]]]] 45 | [DEFINE d [STRUCT [ARRAY [FIELD "h" [_INT]] [FIELD "j" [_STRING]]]]] 46 | [PRINT [TSTRING [VAR a]]] 47 | [PRINT [TSTRING [VAR b]]] 48 | [PRINT [TSTRING [VAR c]]] 49 | [PRINT [TSTRING [VAR d]]] 50 | 51 | [DEFINE e [TEQUAL [VAR c] [VAR d]]] 52 | [IF [VAR e] [PRINT "TRUE"] ELSE [PRINT "FALSE"]] 53 | 54 | [DEFINE f [TEQUAL [VAR c] [VAR c]]] 55 | [IF [VAR f] [PRINT "TRUE"] ELSE [PRINT "FALSE"]] 56 | 57 | # IR 58 | [PRINT "\nIR:"] 59 | [DEFINE bld [newBuilder]] 60 | [DEFINE out [irBuild [VAR bld] [VAR p]]] 61 | [IF [CANCAST [VAR out] error] 62 | # Print errors 63 | [DEFINE i 0] 64 | [WHILE [COMPARE [VAR i] < [LENGTH [GET [VAR bld] errors]]] 65 | [DEFINE err [INDEX [GET [VAR bld] errors] [VAR i]]] 66 | [PRINT [CONCAT [POSSTR [GET [VAR err] pos]] ": " [GET [VAR err] message]]] 67 | [DEFINE i [MATH [VAR i] + 1]] 68 | ] 69 | [FAIL [VAR out]] 70 | ] 71 | [DEFINE ir [CAST [VAR out] ir]] 72 | [PRINT [CONCAT "Node Count: " [STRING [LENGTH [GET [VAR ir] body]]]]] 73 | -------------------------------------------------------------------------------- /bsp/ir/ir.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "bsp/types.bsp"] 2 | [IMPORT "bsp/tokens.bsp"] 3 | [IMPORT "bsp/ir/scope.bsp"] 4 | [IMPORT "bsp/types.bsp"] 5 | 6 | [TYPEDEF nodeKind INT] 7 | [CONSTDEF nodeKindPrint 0] 8 | [CONSTDEF nodeKindConst 1] 9 | [CONSTDEF nodeKindTyped 2] 10 | [CONSTDEF nodeKindMath 3] 11 | [CONSTDEF nodeKindTyped 4] 12 | [TYPEDEF node STRUCT{typ:type, kind:nodeKind, val:ANY, pos:pos}] 13 | 14 | # Nodes 15 | [TYPEDEF printNode node] # arg 16 | [TYPEDEF mathNode STRUCT{lhs:node, op:INT, rhs:node}] 17 | [TYPEDEF constNode ANY] 18 | 19 | # Others 20 | [TYPEDEF param STRUCT{id:INT, name:STRING, typ: type, pos: pos}] 21 | [TYPEDEF function STRUCT{name:STRING, params:ARRAY{param}, retType:type, body:ARRAY{node}, pos:pos, scope:scopeInfo}] 22 | 23 | [TYPEDEF errorLevel INT] 24 | [CONSTDEF errorLevelError 0] 25 | [CONSTDEF errorLevelWarning 1] # TODO: Use this 26 | [TYPEDEF irError STRUCT{level:errorLevel, pos:pos, message:STRING}] 27 | 28 | [TYPEDEF builder STRUCT{scope:scope, body:ARRAY{node}, funcs:MAP{STRING, function}, errors:ARRAY{irError}, imported: MAP{STRING, BOOL}}] 29 | 30 | [FUNC newBuilder [RETURNS builder] 31 | [DEFINE out [MAKE builder]] 32 | [SET [VAR out] body [MAKE ARRAY{node}]] 33 | [SET [VAR out] errors [MAKE ARRAY{irError}]] 34 | [SET [VAR out] imported [MAKE MAP{STRING, BOOL}]] 35 | [SET [VAR out] funcs [MAKE MAP{STRING, function}]] 36 | [DEFINE scope [MAKE scope]] 37 | [SET [VAR scope] frames [MAKE ARRAY{scopeFrame}]] 38 | [SET [VAR scope] variables [MAKE ARRAY{variable}]] 39 | [SET [VAR out] scope [VAR scope]] 40 | [RETURN [VAR out]] 41 | ] 42 | 43 | [FUNC bldErr [PARAM b builder] [PARAM level errorLevel] [PARAM pos pos] [PARAM message STRING] 44 | [DEFINE err [MAKE irError]] 45 | [SET [VAR err] level [VAR level]] 46 | [SET [VAR err] pos [VAR pos]] 47 | [SET [VAR err] message [VAR message]] 48 | [APPEND [GET [VAR b] errors] [VAR err]] 49 | ] 50 | 51 | # Node builders 52 | [TYPEDEF nodeBuilder STRUCT{args:ARRAY{type}, fn:FUNC{builder, pos, ARRAY{node}}ANY}] 53 | [DEFINE nodeBuilders [MAKE MAP{STRING, nodeBuilder}]] 54 | [FUNC addNodeBuilder [PARAM name STRING] [PARAM args ARRAY{type}] [PARAM fn FUNC{builder, pos, ARRAY{node}}ANY] 55 | [DEFINE bld [MAKE nodeBuilder]] 56 | [SET [VAR bld] args [VAR args]] 57 | [SET [VAR bld] fn [VAR fn]] 58 | [SET [VAR nodeBuilders] [VAR name] [VAR bld]] 59 | ] -------------------------------------------------------------------------------- /parser/nodes.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/Nv7-Github/bsharp/tokens" 8 | ) 9 | 10 | type Node interface { 11 | fmt.Stringer 12 | Pos() *tokens.Pos 13 | } 14 | 15 | type CallNode struct { 16 | pos *tokens.Pos 17 | Name string 18 | Args []Node 19 | } 20 | 21 | func (n *CallNode) String() string { 22 | args := &strings.Builder{} 23 | for i, arg := range n.Args { 24 | args.WriteString(arg.String()) 25 | if i < len(n.Args)-1 { 26 | args.WriteString(" ") 27 | } 28 | } 29 | return fmt.Sprintf("(%s)][%s %s]", n.Pos().String(), n.Name, args) 30 | } 31 | 32 | func (n *CallNode) Pos() *tokens.Pos { 33 | return n.pos 34 | } 35 | 36 | type IdentNode struct { 37 | pos *tokens.Pos 38 | Value string 39 | } 40 | 41 | func (i *IdentNode) Pos() *tokens.Pos { 42 | return i.pos 43 | } 44 | 45 | func (i *IdentNode) String() string { 46 | return fmt.Sprintf("Ident(%s, %s)", i.Value, i.Pos().String()) 47 | } 48 | 49 | type BoolNode struct { 50 | pos *tokens.Pos 51 | Value bool 52 | } 53 | 54 | func (b *BoolNode) Pos() *tokens.Pos { 55 | return b.pos 56 | } 57 | 58 | func (b *BoolNode) String() string { 59 | return fmt.Sprintf("Bool(%t, %s)", b.Value, b.Pos().String()) 60 | } 61 | 62 | type NullNode struct { 63 | pos *tokens.Pos 64 | } 65 | 66 | func (n *NullNode) Pos() *tokens.Pos { 67 | return n.pos 68 | } 69 | 70 | func (n *NullNode) String() string { 71 | return fmt.Sprintf("Null(%s)", n.Pos().String()) 72 | } 73 | 74 | type StringNode struct { 75 | pos *tokens.Pos 76 | Value string 77 | } 78 | 79 | func (s *StringNode) Pos() *tokens.Pos { 80 | return s.pos 81 | } 82 | 83 | func (s *StringNode) String() string { 84 | return fmt.Sprintf("String(%q, %s)", s.Value, s.Pos().String()) 85 | } 86 | 87 | type ByteNode struct { 88 | pos *tokens.Pos 89 | Value byte 90 | } 91 | 92 | func (s *ByteNode) Pos() *tokens.Pos { 93 | return s.pos 94 | } 95 | 96 | func (s *ByteNode) String() string { 97 | return fmt.Sprintf("Byte('%c', %s)", s.Value, s.Pos().String()) 98 | } 99 | 100 | type NumberNode struct { 101 | pos *tokens.Pos 102 | Value string 103 | } 104 | 105 | func (n *NumberNode) Pos() *tokens.Pos { 106 | return n.pos 107 | } 108 | 109 | func (n *NumberNode) String() string { 110 | return fmt.Sprintf("Number(%s, %s)", n.Value, n.Pos().String()) 111 | } 112 | -------------------------------------------------------------------------------- /backends/cgen/ops.go: -------------------------------------------------------------------------------- 1 | package cgen 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Nv7-Github/bsharp/ir" 7 | "github.com/Nv7-Github/bsharp/types" 8 | ) 9 | 10 | var mathOps = map[ir.MathOperation]string{ 11 | ir.MathOperationAdd: "+", 12 | ir.MathOperationSub: "-", 13 | ir.MathOperationMul: "*", 14 | ir.MathOperationDiv: "/", 15 | ir.MathOperationMod: "%", 16 | } 17 | 18 | func (c *CGen) addMath(n *ir.MathNode) (*Code, error) { 19 | l, err := c.AddNode(n.Lhs) 20 | if err != nil { 21 | return nil, err 22 | } 23 | r, err := c.AddNode(n.Rhs) 24 | if err != nil { 25 | return nil, err 26 | } 27 | pre := JoinCode(l.Pre, r.Pre) 28 | if n.Op == ir.MathOperationPow { 29 | switch n.Lhs.Type().BasicType() { 30 | case types.INT: 31 | return &Code{ 32 | Pre: pre, 33 | Value: fmt.Sprintf("(long)(pow((double)%s, (double)%s) + 0.5)", l.Value, r.Value), 34 | }, nil 35 | 36 | case types.FLOAT: 37 | return &Code{ 38 | Pre: pre, 39 | Value: fmt.Sprintf("pow(%s, %s)", l.Value, r.Value), 40 | }, nil 41 | } 42 | } 43 | if n.Op == ir.MathOperationMod && types.FLOAT.Equal(n.Lhs.Type()) { // Need to use fmod 44 | return &Code{ 45 | Pre: pre, 46 | Value: fmt.Sprintf("fmod(%s, %s)", l.Value, r.Value), 47 | }, nil 48 | } 49 | return &Code{ 50 | Pre: pre, 51 | Value: fmt.Sprintf("(%s %s %s)", l.Value, mathOps[n.Op], r.Value), 52 | }, nil 53 | } 54 | 55 | var compOpCodes = map[ir.CompareOperation]string{ 56 | ir.CompareOperationEqual: "==", 57 | ir.CompareOperationNotEqual: "!=", 58 | ir.CompareOperationGreater: ">", 59 | ir.CompareOperationGreaterEqual: ">=", 60 | ir.CompareOperationLess: "<", 61 | ir.CompareOperationLessEqual: "<=", 62 | } 63 | 64 | func (c *CGen) addCompare(n *ir.CompareNode) (*Code, error) { 65 | l, err := c.AddNode(n.Lhs) 66 | if err != nil { 67 | return nil, err 68 | } 69 | r, err := c.AddNode(n.Rhs) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | switch n.Lhs.Type().BasicType() { 75 | case types.INT, types.FLOAT, types.BYTE: 76 | return &Code{ 77 | Pre: JoinCode(l.Pre, r.Pre), 78 | Value: fmt.Sprintf("(%s %s %s)", l.Value, compOpCodes[n.Op], r.Value), 79 | }, nil 80 | 81 | case types.STRING: 82 | return &Code{ 83 | Pre: JoinCode(l.Pre, r.Pre), 84 | Value: fmt.Sprintf("string_cmp(%s, %s) %s 0", l.Value, r.Value, compOpCodes[n.Op]), 85 | }, nil 86 | } 87 | 88 | panic("invalid type") 89 | } 90 | -------------------------------------------------------------------------------- /backends/bstar/fns.go: -------------------------------------------------------------------------------- 1 | package bstar 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Nv7-Github/bsharp/ir" 7 | ) 8 | 9 | func (b *BStar) buildFn(fn *ir.Function) (Node, error) { 10 | args := []Node{constNode("FUNC"), constNode(fn.Name)} 11 | pars := make([]Node, len(fn.Params)) 12 | for i, arg := range fn.Params { 13 | pars[i] = constNode(`"` + formatName(fmt.Sprintf("%s%d", arg.Name, arg.ID)) + `"`) 14 | } 15 | args = append(args, blockNode(true, append([]Node{constNode("ARRAY")}, pars...)...)) 16 | body, err := b.buildNodes(fn.Body) 17 | if err != nil { 18 | return nil, err 19 | } 20 | bod := []Node{constNode("BLOCK")} 21 | bod = append(bod, body...) 22 | bod = append(bod, b.noPrintNode()) 23 | args = append(args, blockNode(false, bod...)) 24 | return blockNode(false, args...), nil 25 | } 26 | 27 | func (b *BStar) buildFnMap() Node { 28 | bod := make([]Node, len(b.ir.Funcs)) 29 | i := 0 30 | for _, fn := range b.ir.Funcs { 31 | call := make([]Node, len(fn.Params)+1) 32 | call[0] = constNode(fn.Name) 33 | for j := range fn.Params { 34 | call[j+1] = blockNode(true, constNode("INDEX"), blockNode(true, constNode("VAR"), constNode("args")), constNode(j)) 35 | } 36 | bod[i] = blockNode(false, constNode("IF"), 37 | blockNode(true, constNode("COMPARE"), blockNode(true, constNode("VAR"), constNode("name")), constNode("=="), constNode(fmt.Sprintf("%q", fn.Name))), 38 | blockNode(false, constNode("RETURN"), blockNode(false, call...)), 39 | b.noPrintNode(), 40 | ) 41 | i++ 42 | } 43 | return blockNode(false, constNode("FUNC"), constNode("CALL"), blockNode(true, constNode("ARRAY"), constNode(`"name"`), constNode(`"args"`)), blockNode(false, append([]Node{constNode("BLOCK")}, bod...)...)) 44 | } 45 | 46 | func (b *BStar) buildFnCall(n *ir.FnCallNode) (Node, error) { 47 | v, ok := n.Fn.(*ir.CallNode) 48 | var c *ir.FnNode 49 | if ok { 50 | c, ok = v.Call.(*ir.FnNode) 51 | } 52 | if ok { 53 | args := make([]Node, len(n.Params)) 54 | var err error 55 | for i, par := range n.Params { 56 | args[i], err = b.buildNode(par) 57 | if err != nil { 58 | return nil, err 59 | } 60 | } 61 | 62 | return blockNode(true, append([]Node{constNode(c.Name)}, args...)...), nil 63 | } 64 | 65 | fn, err := b.buildNode(n.Fn) 66 | if err != nil { 67 | return nil, err 68 | } 69 | args := make([]Node, len(n.Params)+1) 70 | args[0] = constNode("ARRAY") 71 | for i, arg := range n.Params { 72 | args[i+1], err = b.buildNode(arg) 73 | if err != nil { 74 | return nil, err 75 | } 76 | } 77 | 78 | return blockNode(true, constNode("CALL"), fn, blockNode(true, args...)), nil 79 | } 80 | -------------------------------------------------------------------------------- /bot/lb.go: -------------------------------------------------------------------------------- 1 | package bot 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | 7 | "github.com/Nv7-Github/bsharp/bot/db" 8 | "github.com/Nv7-Github/sevcord" 9 | ) 10 | 11 | const ItemsPerPage = 10 12 | 13 | func LbCmd(b *Bot) sevcord.SlashCommandObject { 14 | return &sevcord.SlashCommand{ 15 | Name: "lb", 16 | Description: "View the most used tags!", 17 | Options: []sevcord.Option{}, 18 | Handler: b.LbUsedCmd, 19 | } 20 | } 21 | 22 | func (b *Bot) LbUsedCmd(ctx sevcord.Ctx, args []any) { 23 | dat, err := b.Get(ctx.Guild()) 24 | if Error(ctx, err) { 25 | return 26 | } 27 | 28 | ctx.Acknowledge() 29 | 30 | // Load progs 31 | progs := make([]*db.Program, len(dat.Programs)) 32 | dat.RLock() 33 | i := 0 34 | for _, p := range dat.Programs { 35 | progs[i] = p 36 | i++ 37 | } 38 | dat.RUnlock() 39 | 40 | // Sort 41 | sort.Slice(progs, func(i, j int) bool { 42 | return progs[i].Uses > progs[j].Uses 43 | }) 44 | 45 | // Calculate stats 46 | pages := len(progs) / ItemsPerPage 47 | if len(progs)%ItemsPerPage != 0 { 48 | pages++ 49 | } 50 | page := 0 51 | 52 | // Send 53 | buildEmb := func() *sevcord.Response { 54 | desc := "" 55 | 56 | for i := 0; i < ItemsPerPage; i++ { 57 | ind := i + page*ItemsPerPage 58 | if ind >= len(progs) { 59 | break 60 | } 61 | s := "s" 62 | uses := progs[ind].Uses 63 | if uses == 1 { 64 | s = "" 65 | } 66 | desc += fmt.Sprintf("%d. **%s** - %d Use%s\n", i+1, progs[ind].Name, uses, s) 67 | } 68 | 69 | return sevcord.EmbedResponse(sevcord.NewEmbedBuilder("Most Used Tags"). 70 | Color(15844367). 71 | Footer(fmt.Sprintf("Page %d/%d", page+1, pages), ""). 72 | Description(desc)) 73 | } 74 | 75 | // Buttons 76 | var btns []sevcord.Component 77 | btns = []sevcord.Component{ 78 | &sevcord.Button{ 79 | Emoji: sevcord.ComponentEmojiCustom("leftarrow", "861722690813165598", false), 80 | Style: sevcord.ButtonStylePrimary, 81 | Handler: func(ctx sevcord.Ctx) { 82 | page-- 83 | if page < 0 { 84 | page = pages - 1 85 | } 86 | ctx.Respond(buildEmb().ComponentRow(btns...)) 87 | }, 88 | }, 89 | &sevcord.Button{ 90 | Emoji: sevcord.ComponentEmojiCustom("rightarrow", "861722690926936084", false), 91 | Style: sevcord.ButtonStylePrimary, 92 | Handler: func(ctx sevcord.Ctx) { 93 | page++ 94 | if page >= pages { 95 | page = 0 96 | } 97 | ctx.Respond(buildEmb().ComponentRow(btns...)) 98 | }, 99 | }, 100 | } 101 | 102 | // Send 103 | ctx.Respond(buildEmb().ComponentRow(btns...)) 104 | } 105 | -------------------------------------------------------------------------------- /bpp/funcs/math.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "bpp/interp.bsp"] 2 | [IMPORT "bpp/funcs/funcs.bsp"] 3 | [IMPORT "math.bsp"] 4 | 5 | [FUNC node_choose [PARAM self node] [PARAM args ARRAY{node}] [RETURNS ANY] 6 | [DEFINE arr [VAR args]] 7 | [IF [CANCAST [GET [INDEX [VAR args] 0] val] ARRAY{node}] 8 | [DEFINE arr [CAST [GET [INDEX [VAR args] 0] val] ARRAY{node}]] 9 | ] 10 | [DEFINE ind [RANDINT 0 [LENGTH [VAR arr]]]] 11 | [RETURN [ANY [INDEX [VAR arr] [VAR ind]]]] 12 | ] 13 | 14 | [ADD_BLD "CHOOSE" [FN node_choose]] 15 | 16 | [FUNC node_choosechar [PARAM self node] [PARAM args ARRAY{node}] [RETURNS ANY] 17 | [DEFINE str [CAST [GET [INDEX [VAR args] 0] val] STRING]] 18 | [DEFINE ind [RANDINT 0 [LENGTH [VAR str]]]] 19 | 20 | [DEFINE out [MAKE node]] 21 | [SET [VAR out] kind [CONST nodeKindString]] 22 | [SET [VAR out] val [ANY [STRING [INDEX [VAR str] [VAR ind]]]]] 23 | [RETURN [ANY [VAR out]]] 24 | ] 25 | 26 | [ADD_BLD "CHOOSECHAR" [FN node_choosechar]] 27 | 28 | [FUNC node_randint [PARAM self node] [PARAM args ARRAY{node}] [RETURNS ANY] 29 | [DEFINE lower [CAST [GET [INDEX [VAR args] 0] val] INT]] 30 | [DEFINE upper [CAST [GET [INDEX [VAR args] 1] val] INT]] 31 | [DEFINE val [RANDINT [VAR lower] [VAR upper]]] 32 | 33 | [DEFINE out [MAKE node]] 34 | [SET [VAR out] kind [CONST nodeKindInt]] 35 | [SET [VAR out] val [ANY [VAR val]]] 36 | [RETURN [ANY [VAR out]]] 37 | ] 38 | 39 | [ADD_BLD "RANDINT" [FN node_randint]] 40 | 41 | [FUNC node_random [PARAM self node] [PARAM args ARRAY{node}] [RETURNS ANY] 42 | [DEFINE lower [FLOAT 0]] 43 | [IF [COMPARE [GET [INDEX [VAR args] 0] kind] == [CONST nodeKindInt]] 44 | [DEFINE lower [FLOAT [CAST [GET [INDEX [VAR args] 0] val] INT]]] 45 | ELSE 46 | [DEFINE lower [CAST [GET [INDEX [VAR args] 0] val] FLOAT]] 47 | ] 48 | [DEFINE upper [FLOAT 0]] 49 | [IF [COMPARE [GET [INDEX [VAR args] 1] kind] == [CONST nodeKindInt]] 50 | [DEFINE upper [FLOAT [CAST [GET [INDEX [VAR args] 1] val] INT]]] 51 | ELSE 52 | [DEFINE upper [CAST [GET [INDEX [VAR args] 1] val] FLOAT]] 53 | ] 54 | [DEFINE val [RANDOM [VAR lower] [VAR upper]]] 55 | 56 | [DEFINE out [MAKE node]] 57 | [SET [VAR out] kind [CONST nodeKindFloat]] 58 | [SET [VAR out] val [ANY [VAR val]]] 59 | [RETURN [ANY [VAR out]]] 60 | ] 61 | 62 | [ADD_BLD "RANDOM" [FN node_random]] 63 | 64 | [FUNC node_floor [PARAM self node] [PARAM args ARRAY{node}] [RETURNS ANY] 65 | [DEFINE val [CAST [GET [INDEX [VAR args] 0] val] FLOAT]] 66 | 67 | [DEFINE node [MAKE node]] 68 | [SET [VAR node] kind [CONST nodeKindInt]] 69 | [SET [VAR node] val [ANY [FLOOR [VAR val]]]] 70 | [RETURN [ANY [VAR node]]] 71 | ] 72 | 73 | [ADD_BLD "FLOOR" [FN node_floor]] -------------------------------------------------------------------------------- /bpp/funcs/funcs.bsp: -------------------------------------------------------------------------------- 1 | # TODO: Error checking in all the funcs when indexing args, casting, etc. 2 | [IMPORT "bpp/parser.bsp"] 3 | [IMPORT "errors.bsp"] 4 | 5 | [FUNC NODE_ERR [PARAM node node] [PARAM msg STRING] [RETURNS ANY] 6 | [RETURN [ERROR [CONCAT "Error at \"" [NODE_STRING [VAR node]] "\": " [VAR msg]]]] 7 | ] 8 | 9 | [FUNC EMPTY_NODE [RETURNS node] 10 | [DEFINE out [MAKE node]] 11 | [SET [VAR out] kind [CONST nodeKindString]] 12 | [SET [VAR out] val [ANY ""]] 13 | [RETURN [VAR out]] 14 | ] 15 | 16 | [DEFINE blds [MAKE MAP{STRING, FUNC{node, ARRAY{node}}ANY}]] 17 | [DEFINE blks [MAKE MAP{STRING, FUNC{node, ARRAY{node}}ANY}]] 18 | 19 | [FUNC ADD_BLD [PARAM name STRING] [PARAM fn FUNC{node, ARRAY{node}}ANY] 20 | [SET [VAR blds] [VAR name] [VAR fn]] 21 | ] 22 | 23 | [FUNC ADD_BLK [PARAM name STRING] [PARAM fn FUNC{node, ARRAY{node}}ANY] 24 | [SET [VAR blks] [VAR name] [VAR fn]] 25 | ] 26 | 27 | # Basic funcs 28 | [FUNC node_array [PARAM self node] [PARAM args ARRAY{node}] [RETURNS ANY] 29 | [DEFINE out [MAKE node]] 30 | [SET [VAR out] kind [CONST nodeKindArray]] 31 | [SET [VAR out] val [ANY [VAR args]]] 32 | [RETURN [ANY [VAR out]]] 33 | ] 34 | 35 | [ADD_BLD "ARRAY" [FN node_array]] 36 | 37 | [FUNC node_comment [PARAM self node] [PARAM args ARRAY{node}] [RETURNS ANY] 38 | [RETURN [ANY [EMPTY_NODE]]] 39 | ] 40 | 41 | [ADD_BLD "#" [FN node_comment]] 42 | 43 | [FUNC node_concat [PARAM self node] [PARAM args ARRAY{node}] [RETURNS ANY] 44 | [DEFINE out ""] 45 | [DEFINE i 0] 46 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR args]]] 47 | [DEFINE out [CONCAT [VAR out] [NODE_STRING [INDEX [VAR args] [VAR i]]]]] 48 | [DEFINE i [MATH [VAR i] + 1]] 49 | ] 50 | 51 | [DEFINE node [MAKE node]] 52 | [SET [VAR node] kind [CONST nodeKindString]] 53 | [SET [VAR node] val [ANY [VAR out]]] 54 | [RETURN [ANY [VAR node]]] 55 | ] 56 | 57 | [ADD_BLD "CONCAT" [FN node_concat]] 58 | 59 | [FUNC node_index [PARAM self node] [PARAM args ARRAY{node}] [RETURNS ANY] 60 | [DEFINE arg [INDEX [VAR args] 0]] 61 | [DEFINE ind [CAST [GET [INDEX [VAR args] 1] val] INT]] 62 | [SWITCH [GET [VAR arg] kind] 63 | [CASE [CONST nodeKindString] 64 | [DEFINE val [INDEX [CAST [GET [VAR arg] val] STRING] [VAR ind]]] 65 | [DEFINE node [MAKE node]] 66 | [SET [VAR node] kind [CONST nodeKindString]] 67 | [SET [VAR node] val [ANY [STRING [VAR val]]]] 68 | [RETURN [ANY [VAR node]]] 69 | ] 70 | 71 | [CASE [CONST nodeKindArray] 72 | [DEFINE val [INDEX [CAST [GET [VAR arg] val] ARRAY{node}] [VAR ind]]] 73 | [RETURN [ANY [VAR val]]] 74 | ] 75 | ] 76 | 77 | [RETURN [ANY [EMPTY_NODE]]] 78 | ] 79 | 80 | [ADD_BLD "INDEX" [FN node_index]] -------------------------------------------------------------------------------- /backends/bsp/bsp.go: -------------------------------------------------------------------------------- 1 | package bsp 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/Nv7-Github/bsharp/ir" 8 | "github.com/Nv7-Github/bsharp/types" 9 | ) 10 | 11 | type BSP struct { 12 | ir *ir.IR 13 | Config *BSPConfig 14 | } 15 | 16 | type BSPConfig struct { 17 | Seperator string 18 | Tab string 19 | Newline string 20 | } 21 | 22 | func NewBSP(i *ir.IR) *BSP { 23 | return &BSP{ 24 | ir: i, 25 | Config: &BSPConfig{ 26 | Seperator: " ", 27 | Tab: "\t", 28 | Newline: "\n", 29 | }, 30 | } 31 | } 32 | 33 | func (b *BSP) Build() (string, error) { 34 | out := &strings.Builder{} 35 | 36 | // Move global vars to the top 37 | toRemove := make([]int, 0) 38 | pre := make([]ir.Node, 0) 39 | for i, v := range b.ir.Body { 40 | c, ok := v.(*ir.CallNode) 41 | if !ok { 42 | continue 43 | } 44 | d, ok := c.Call.(*ir.DefineNode) 45 | if !ok { 46 | continue 47 | } 48 | if b.ir.Variables[d.Var].NeedsGlobal { 49 | toRemove = append(toRemove, i) 50 | pre = append(pre, v) 51 | } 52 | } 53 | 54 | // Remove those definitions 55 | newBody := make([]ir.Node, 0, len(b.ir.Body)-len(toRemove)) 56 | for i, v := range b.ir.Body { 57 | if len(toRemove) > 0 && i == toRemove[0] { 58 | toRemove = toRemove[1:] 59 | continue 60 | } 61 | newBody = append(newBody, v) 62 | } 63 | b.ir.Body = newBody 64 | 65 | // Add pre 66 | for _, n := range pre { 67 | v, err := b.buildNode(n) 68 | if err != nil { 69 | return "", err 70 | } 71 | out.WriteString(v) 72 | out.WriteString(b.Config.Newline) 73 | } 74 | 75 | // Add functions 76 | for _, fn := range b.ir.Funcs { 77 | fmt.Fprintf(out, "[FUNC%s%s", b.Config.Seperator, fn.Name) 78 | for _, p := range fn.Params { 79 | fmt.Fprintf(out, "%s[PARAM%s%s%s%s]", b.Config.Seperator, b.Config.Seperator, p.Name, b.Config.Seperator, p.Type.String()) 80 | } 81 | if !types.NULL.Equal(fn.RetType) { 82 | fmt.Fprintf(out, "%s[RETURNS%s%s]", b.Config.Seperator, b.Config.Seperator, fn.RetType.String()) 83 | } 84 | out.WriteString(b.Config.Newline) 85 | for _, n := range fn.Body { 86 | v, err := b.buildNode(n) 87 | if err != nil { 88 | return "", err 89 | } 90 | out.WriteString(b.Tab(v)) 91 | out.WriteString(b.Config.Newline) 92 | } 93 | out.WriteString("]" + b.Config.Newline) 94 | } 95 | 96 | // Add body 97 | for _, n := range b.ir.Body { 98 | v, err := b.buildNode(n) 99 | if err != nil { 100 | return "", err 101 | } 102 | out.WriteString(v) 103 | out.WriteString(b.Config.Newline) 104 | } 105 | return out.String(), nil 106 | } 107 | -------------------------------------------------------------------------------- /bsharp-lsp/diagnostics.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "strings" 7 | 8 | "github.com/Nv7-Github/bsharp/parser" 9 | "github.com/Nv7-Github/bsharp/tokens" 10 | "github.com/tliron/glsp" 11 | protocol "github.com/tliron/glsp/protocol_3_16" 12 | ) 13 | 14 | var Root string 15 | var RootURI string 16 | 17 | type FS struct{} 18 | 19 | func (d *FS) Parse(name string) (*parser.Parser, error) { 20 | var src string 21 | doc, exists := Documents[filepath.Join(RootURI, name)] 22 | if exists { 23 | src = doc.Source 24 | } else { 25 | var err error 26 | dat, err := os.ReadFile(filepath.Join(Root, name)) 27 | if err != nil { 28 | return nil, err 29 | } 30 | src = string(dat) 31 | } 32 | stream := tokens.NewTokenizer(tokens.NewStream(name, src)) 33 | err := stream.Tokenize() 34 | if err != nil { 35 | return nil, err 36 | } 37 | parser := parser.NewParser(stream) 38 | 39 | err = parser.Parse() 40 | return parser, err 41 | } 42 | 43 | func textDocumentDidSave(context *glsp.Context, params *protocol.DidSaveTextDocumentParams) error { 44 | // Run when doc is saved 45 | doc := Documents[params.TextDocument.URI] 46 | path := strings.TrimPrefix(params.TextDocument.URI, RootURI+"/") 47 | fs := &FS{} 48 | p, err := fs.Parse(path) 49 | if err != nil { 50 | return nil // Invalid code, don't run diagnostics 51 | } 52 | ir := getBld(doc) 53 | err = ir.Build(p, fs) 54 | if err == nil { // No error, save IR cache, clear diagnostics 55 | doc.IRCache = ir.IR() 56 | 57 | // Clear diagnostics 58 | context.Notify(protocol.ServerTextDocumentPublishDiagnostics, protocol.PublishDiagnosticsParams{ 59 | URI: params.TextDocument.URI, 60 | Diagnostics: []protocol.Diagnostic{}, 61 | }) 62 | return nil 63 | } 64 | 65 | if len(ir.Errors) == 0 { 66 | return nil 67 | } 68 | diagnostics := make(map[string][]protocol.Diagnostic) 69 | for _, e := range ir.Errors { 70 | if diagnostics[e.Pos.File] == nil { 71 | diagnostics[e.Pos.File] = make([]protocol.Diagnostic, 0) 72 | } 73 | diagnostics[e.Pos.File] = append(diagnostics[e.Pos.File], protocol.Diagnostic{ 74 | Range: protocol.Range{ 75 | Start: protocol.Position{Line: uint32(e.Pos.Line), Character: uint32(e.Pos.Char)}, 76 | End: protocol.Position{Line: uint32(e.Pos.EndLine), Character: uint32(e.Pos.EndChar)}, 77 | }, 78 | Severity: Ptr(protocol.DiagnosticSeverityError), 79 | Source: Ptr("bsharp"), 80 | Message: e.Message, 81 | }) 82 | } 83 | for k, v := range diagnostics { 84 | context.Notify(protocol.ServerTextDocumentPublishDiagnostics, protocol.PublishDiagnosticsParams{ 85 | URI: filepath.Join(RootURI, k), 86 | Diagnostics: v, 87 | }) 88 | } 89 | 90 | return nil 91 | } 92 | -------------------------------------------------------------------------------- /types/tokenize.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Tokenize types 8 | type tokenType int 9 | 10 | const ( 11 | tokenTypeConst tokenType = iota // ARRAY, MAP, FUNC, STRING, INT, FLOAT, BOOL, NULL 12 | tokenTypeLBrack 13 | tokenTypeRBrack 14 | tokenTypeComma 15 | tokenTypeIdent 16 | tokenTypeColon 17 | ) 18 | 19 | // ARRAY{FUNC{INT,INT}INT} 20 | type token struct { 21 | typ tokenType 22 | value *string 23 | } 24 | 25 | func (t token) String() string { 26 | switch t.typ { 27 | case tokenTypeConst: 28 | return *t.value 29 | case tokenTypeLBrack: 30 | return "{" 31 | case tokenTypeRBrack: 32 | return "}" 33 | case tokenTypeComma: 34 | return "," 35 | case tokenTypeIdent: 36 | return *t.value 37 | case tokenTypeColon: 38 | return ":" 39 | default: 40 | return "" 41 | } 42 | } 43 | 44 | var constTokens = [][]rune{ 45 | []rune("ARRAY"), 46 | []rune("MAP"), 47 | []rune("FUNC"), 48 | []rune("STRING"), 49 | []rune("INT"), 50 | []rune("BYTE"), 51 | []rune("FLOAT"), 52 | []rune("BOOL"), 53 | []rune("NIL"), 54 | []rune("STRUCT"), 55 | []rune("ANY"), 56 | } 57 | 58 | var tokenStarters = map[rune]struct{}{} 59 | 60 | func init() { 61 | for _, v := range constTokens { 62 | tokenStarters[[]rune(v)[0]] = struct{}{} 63 | } 64 | } 65 | 66 | func tokenize(val []rune) ([]token, error) { 67 | tokens := make([]token, 0) 68 | for i := 0; i < len(val); i++ { 69 | char := val[i] 70 | switch char { 71 | case '{': 72 | tokens = append(tokens, token{tokenTypeLBrack, nil}) 73 | 74 | case '}': 75 | tokens = append(tokens, token{tokenTypeRBrack, nil}) 76 | 77 | case ',': 78 | tokens = append(tokens, token{tokenTypeComma, nil}) 79 | 80 | case ':': 81 | tokens = append(tokens, token{tokenTypeColon, nil}) 82 | 83 | case ' ', '\t', '\n', '\r': 84 | continue 85 | 86 | default: 87 | if _, ok := tokenStarters[char]; ok { 88 | // Check if token is a const 89 | for _, v := range constTokens { 90 | s := string(v) 91 | if strings.HasPrefix(string(val[i:]), s) { 92 | tokens = append(tokens, token{tokenTypeConst, &s}) 93 | i += len(v) - 1 94 | break 95 | } 96 | } 97 | } else { 98 | // add ident 99 | ident := "" 100 | loop: 101 | for i < len(val) { 102 | switch char { 103 | case ',', ':', '}', '{', ' ', '\t', '\n': 104 | break loop 105 | } 106 | ident += string(char) 107 | if i >= len(val) { 108 | break 109 | } 110 | i++ 111 | if i < len(val) { 112 | char = val[i] 113 | } 114 | } 115 | 116 | tokens = append(tokens, token{tokenTypeIdent, &ident}) 117 | i-- 118 | } 119 | } 120 | } 121 | 122 | return tokens, nil 123 | } 124 | -------------------------------------------------------------------------------- /bpp/interp.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "bpp/parser.bsp"] 2 | [IMPORT "errors.bsp"] 3 | [IMPORT "bpp/funcs/funcs.bsp"] 4 | 5 | [FUNC evalNode [PARAM node node] [RETURNS ANY] 6 | [SWITCH [GET [VAR node] kind] 7 | [CASE [CONST nodeKindTag] 8 | # Check for valid tag syntax 9 | [DEFINE args [CAST [GET [VAR node] val] ARRAY{node}]] 10 | [IF [COMPARE [LENGTH [VAR args]] == 0] 11 | [RETURN [NODE_ERR [VAR node] "tag must have at least 1 argument"]] 12 | ] 13 | [IF [COMPARE [GET [INDEX [VAR args] 0] kind] != [CONST nodeKindString]] 14 | [RETURN [NODE_ERR [INDEX [VAR args] 0] "expected STRING for tag name"]] 15 | ] 16 | 17 | # Get name 18 | [DEFINE name [CAST [GET [INDEX [VAR args] 0] val] STRING]] 19 | 20 | # Check if blk, if so do that without building args 21 | [IF [EXISTS [VAR blks] [VAR name]] 22 | [DEFINE blk [GET [VAR blks] [VAR name]]] 23 | [RETURN [CALL [VAR blk] [VAR node] [VAR args]]] 24 | ] 25 | 26 | # Build params 27 | [DEFINE pars [MAKE ARRAY{node}]] 28 | [DEFINE i 1] 29 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR args]]] 30 | [DEFINE err [evalNode [INDEX [VAR args] [VAR i]]]] 31 | [IF [CANCAST [VAR err] error] 32 | [RETURN [VAR err]] 33 | ] 34 | [APPEND [VAR pars] [CAST [VAR err] node]] 35 | [DEFINE i [MATH [VAR i] + 1]] 36 | ] 37 | 38 | # Call 39 | [IF [NOT [EXISTS [VAR blds] [VAR name]]] 40 | [RETURN [NODE_ERR [VAR node] [CONCAT "unknown tag: \"" [VAR name] "\""]]] 41 | ] 42 | [DEFINE bld [GET [VAR blds] [VAR name]]] 43 | [RETURN [CALL [VAR bld] [VAR node] [VAR pars]]] 44 | ] 45 | 46 | [CASE [CONST nodeKindArray] 47 | [DEFINE args [MAKE ARRAY{node}]] 48 | [DEFINE curr [CAST [GET [VAR node] val] ARRAY{node}]] 49 | [DEFINE i 0] 50 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR curr]]] 51 | [DEFINE err [evalNode [VAR node]]] 52 | [IF [CANCAST [VAR err] error] 53 | [RETURN [VAR err]] 54 | ] 55 | [APPEND [VAR args] [CAST [VAR err] node]] 56 | [DEFINE i [MATH [VAR i] + 1]] 57 | ] 58 | [DEFINE out [MAKE node]] 59 | [SET [VAR out] kind [CONST nodeKindArray]] 60 | [SET [VAR out] val [ANY [VAR args]]] 61 | [RETURN [ANY [VAR out]]] 62 | ] 63 | ] 64 | [RETURN [ANY [VAR node]]] 65 | ] 66 | 67 | [FUNC INTERP [PARAM parsed ARRAY{node}] [RETURNS ARRAY{node}] 68 | [DEFINE out [MAKE ARRAY{node}]] 69 | [DEFINE i 0] 70 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR parsed]]] 71 | [DEFINE err [evalNode [INDEX [VAR parsed] [VAR i]]]] 72 | [IF [CANCAST [VAR err] error] 73 | [FAIL [VAR err]] 74 | ] 75 | [APPEND [VAR out] [CAST [VAR err] node]] 76 | [DEFINE i [MATH [VAR i] + 1]] 77 | ] 78 | [RETURN [VAR out]] 79 | ] -------------------------------------------------------------------------------- /backends/cgen/any.go: -------------------------------------------------------------------------------- 1 | package cgen 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/Nv7-Github/bsharp/ir" 8 | "github.com/Nv7-Github/bsharp/types" 9 | ) 10 | 11 | func (c *CGen) typID(typ types.Type) int { 12 | name := typName(typ) 13 | id, exists := c.typIDs[name] 14 | if !exists { 15 | id = len(c.typIDs) 16 | c.typIDs[name] = id 17 | c.idTyps = append(c.idTyps, typ) 18 | } 19 | return id 20 | } 21 | 22 | func (c *CGen) typCode() string { 23 | out := &strings.Builder{} 24 | out.WriteString("const char* const anytyps[] = {\n") 25 | for _, t := range c.idTyps { 26 | fmt.Fprintf(out, "%s%q,\n", c.Config.Tab, t.String()) 27 | } 28 | out.WriteString("};") 29 | return out.String() 30 | } 31 | 32 | func (c *CGen) addAnyCast(n *ir.CastNode) (*Code, error) { 33 | v, err := c.AddNode(n.Value) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | freefn := "NULL" 39 | pre := v.Pre 40 | // Need to be able to get pointer 41 | name := c.GetTmp("cnst") 42 | pre = JoinCode(pre, fmt.Sprintf("%s %s = %s;", c.CType(n.Value.Type()), name, v.Value)) 43 | v.Value = name 44 | 45 | if isDynamic(n.Value.Type()) { 46 | // Grab value 47 | pre = JoinCode(pre, c.GrabCode(v.Value, n.Value.Type())) 48 | 49 | // Create free fn if not added 50 | name := typName(n.Value.Type()) + "_anyfree" 51 | _, exists := c.addedFns[name] 52 | if !exists { 53 | // Add type 54 | c.CType(n.Value.Type()) 55 | 56 | fmt.Fprintf(c.globalfns, "void %s(void* val) {\n", name) 57 | fmt.Fprintf(c.globalfns, "%s%s v = *((%s*)val);\n", c.Config.Tab, c.CType(n.Value.Type()), c.CType(n.Value.Type())) 58 | fmt.Fprintf(c.globalfns, "%s%s;\n", c.Config.Tab, c.FreeCode("v", n.Value.Type())) 59 | c.globalfns.WriteString("}\n\n") 60 | c.addedFns[name] = struct{}{} 61 | } 62 | freefn = name 63 | } 64 | 65 | a := c.GetTmp("any") 66 | code := fmt.Sprintf("any* %s = any_new((void*)(&%s), sizeof(%s), %d, %s);\n", a, v.Value, c.CType(n.Value.Type()), c.typID(n.Value.Type()), freefn) 67 | c.stack.Add(c.FreeCode(a, types.ANY)) 68 | return &Code{ 69 | Pre: JoinCode(pre, code), 70 | Value: a, 71 | }, nil 72 | } 73 | 74 | func (c *CGen) addCanCast(n *ir.CanCastNode) (*Code, error) { 75 | a, err := c.AddNode(n.Value) 76 | if err != nil { 77 | return nil, err 78 | } 79 | t := c.typID(n.Typ) 80 | return &Code{ 81 | Pre: a.Pre, 82 | Value: fmt.Sprintf("(%s->typ == %d)", a.Value, t), 83 | }, nil 84 | } 85 | 86 | func (c *CGen) addAnyFromCast(n *ir.CastNode) (*Code, error) { 87 | a, err := c.AddNode(n.Value) 88 | if err != nil { 89 | return nil, err 90 | } 91 | pre := JoinCode(a.Pre, fmt.Sprintf("any_try_cast(%d, %s, %d);", c.posID(n.Pos()), a.Value, c.typID(n.Type()))) 92 | return &Code{ 93 | Pre: pre, 94 | Value: fmt.Sprintf("(*((%s*)(%s->value)))", c.CType(n.Type()), a.Value), 95 | }, nil 96 | } 97 | -------------------------------------------------------------------------------- /old/ssa/ssagen/block.go: -------------------------------------------------------------------------------- 1 | package ssagen 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Nv7-Github/bsharp/ir" 7 | "github.com/Nv7-Github/bsharp/old/ssa" 8 | "github.com/Nv7-Github/bsharp/tokens" 9 | ) 10 | 11 | func (s *SSAGen) addIf(pos *tokens.Pos, n *ir.IfNode) ssa.ID { 12 | cond := s.Add(n.Condition) 13 | body := s.ssa.NewBlock(s.ssa.BlockName("ifbody"), pos) 14 | end := s.ssa.NewBlock(s.ssa.BlockName("ifend"), pos) 15 | if n.Else != nil { 16 | els := s.ssa.NewBlock(s.ssa.BlockName("ifelse"), pos) 17 | s.blk.EndInstrutionCondJmp(cond, body, els) 18 | 19 | s.blk = body 20 | for _, node := range n.Body { 21 | s.Add(node) 22 | } 23 | s.blk.EndInstrutionJmp(end) 24 | 25 | s.blk = els 26 | for _, node := range n.Else { 27 | s.Add(node) 28 | } 29 | s.blk.EndInstrutionJmp(end) 30 | 31 | s.blk = end 32 | } else { 33 | s.blk.EndInstrutionCondJmp(cond, body, end) 34 | s.blk = body 35 | for _, node := range n.Body { 36 | s.Add(node) 37 | } 38 | s.blk.EndInstrutionJmp(end) 39 | s.blk = end 40 | } 41 | return ssa.NullID() 42 | } 43 | 44 | func (s *SSAGen) addWhile(pos *tokens.Pos, n *ir.WhileNode) ssa.ID { 45 | pre := s.ssa.NewBlock(s.ssa.BlockName("whilepre"), pos) 46 | s.blk.EndInstrutionJmp(pre) 47 | s.blk = pre 48 | cond := s.Add(n.Condition) 49 | 50 | body := s.ssa.NewBlock(s.ssa.BlockName("whilebody"), pos) 51 | end := s.ssa.NewBlock(s.ssa.BlockName("whileend"), pos) 52 | pre.EndInstrutionCondJmp(cond, body, end) 53 | 54 | s.blk = body 55 | for _, node := range n.Body { 56 | s.Add(node) 57 | } 58 | body.EndInstrutionJmp(pre) 59 | 60 | s.blk = end 61 | return ssa.NullID() 62 | } 63 | 64 | func (s *SSAGen) addSwitch(pos *tokens.Pos, n *ir.SwitchNode) ssa.ID { 65 | cond := s.Add(n.Value) 66 | blk := s.blk 67 | cases := make([]ssa.EndInstructionCase, len(n.Cases)) 68 | 69 | end := s.ssa.NewBlock(s.ssa.BlockName("switchend"), pos) 70 | def := end.Label 71 | for i, cs := range n.Cases { 72 | s.blk = s.ssa.NewBlock(s.ssa.BlockName(fmt.Sprintf("switchcase%d_", i)), pos) 73 | c := cs.Block.(*ir.Case) 74 | for _, node := range c.Body { 75 | s.Add(node) 76 | } 77 | s.blk.EndInstrutionJmp(end) 78 | 79 | // Add case 80 | cases[i] = ssa.EndInstructionCase{ 81 | Cond: &ssa.Const{Typ: c.Value.Type(), Value: c.Value.Value}, 82 | Label: s.blk.Label, 83 | } 84 | } 85 | 86 | // Add default 87 | if n.Default != nil { 88 | s.blk = s.ssa.NewBlock(s.ssa.BlockName("switchdefault"), pos) 89 | for _, node := range n.Default.Block.(*ir.Default).Body { 90 | s.Add(node) 91 | } 92 | s.blk.EndInstrutionJmp(end) 93 | def = s.blk.Label 94 | } 95 | 96 | // Go to end 97 | s.blk = end 98 | blk.EndInstructionSwitch(cond, def, cases) 99 | return ssa.NullID() 100 | } 101 | -------------------------------------------------------------------------------- /backends/bsp/stmts.go: -------------------------------------------------------------------------------- 1 | package bsp 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | ) 6 | 7 | func (b *BSP) buildNode(node ir.Node) (string, error) { 8 | switch n := node.(type) { 9 | case *ir.CallNode: 10 | switch c := n.Call.(type) { 11 | case *ir.PrintNode: 12 | return b.addCall("PRINT", c) 13 | 14 | case *ir.MathNode: 15 | return b.addCall("MATH", c) 16 | 17 | case *ir.DefineNode: 18 | return b.addCall("DEFINE", c) 19 | 20 | case *ir.TimeNode: 21 | return b.addCall("TIME", c) 22 | 23 | case *ir.FnNode: 24 | return b.addCall("FN", c) 25 | 26 | case *ir.VarNode: 27 | return b.addCall("VAR", c) 28 | 29 | case *ir.ConcatNode: 30 | return b.addCall("CONCAT", c) 31 | 32 | case *ir.CompareNode: 33 | return b.addCall("COMPARE", c) 34 | 35 | case *ir.IndexNode: 36 | return b.addCall("INDEX", c) 37 | 38 | case *ir.LengthNode: 39 | return b.addCall("LENGTH", c) 40 | 41 | case *ir.MakeNode: 42 | return b.addCall("MAKE", c) 43 | 44 | case *ir.SetNode: 45 | return b.addCall("SET", c) 46 | 47 | case *ir.GetNode: 48 | return b.addCall("GET", c) 49 | 50 | case *ir.ArrayNode: 51 | return b.addCall("ARRAY", c) 52 | 53 | case *ir.AppendNode: 54 | return b.addCall("APPEND", c) 55 | 56 | case *ir.ReturnNode: 57 | return b.addCall("RETURN", c) 58 | 59 | case *ir.CanCastNode: 60 | return b.addCall("CANCAST", c) 61 | 62 | case *ir.PanicNode: 63 | return b.addCall("PANIC", c) 64 | 65 | case *ir.SetStructNode: 66 | return b.addCall("SET", c) 67 | 68 | case *ir.GetStructNode: 69 | return b.addCall("GET", c) 70 | 71 | case *ir.SliceNode: 72 | return b.addCall("SLICE", c) 73 | 74 | case *ir.SetIndexNode: 75 | return b.addCall("SET", c) 76 | 77 | case *ir.ExistsNode: 78 | return b.addCall("EXISTS", c) 79 | 80 | case *ir.LogicalOpNode: 81 | return b.addCall(c.Op.String(), c) 82 | 83 | case *ir.CastNode: 84 | return b.addCast(c) 85 | 86 | case *ir.FnCallNode: 87 | return b.addFnCall(c) 88 | 89 | default: 90 | return "", node.Pos().Error("unsupported call type: %T", c) 91 | } 92 | 93 | case *ir.Const: 94 | return b.addConst(n) 95 | 96 | case *ir.FnCallNode: 97 | return b.addFnCall(n) 98 | 99 | case *ir.CastNode: 100 | return b.addCast(n) 101 | 102 | case *ir.BlockNode: 103 | switch bl := n.Block.(type) { 104 | case *ir.IfNode: 105 | return b.buildIf(bl) 106 | 107 | case *ir.WhileNode: 108 | return b.buildWhile(bl) 109 | 110 | case *ir.SwitchNode: 111 | return b.buildSwitch(bl) 112 | 113 | default: 114 | return "", node.Pos().Error("unsupported block type: %T", bl) 115 | } 116 | 117 | default: 118 | return "", n.Pos().Error("unknown node type: %T", node) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /ir/util.go: -------------------------------------------------------------------------------- 1 | package ir 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/tokens" 5 | "github.com/Nv7-Github/bsharp/types" 6 | ) 7 | 8 | type NullCall struct{} 9 | 10 | func (n NullCall) Type() types.Type { return types.NULL } 11 | 12 | func (b *Builder) MatchTypes(pos *tokens.Pos, args []Node, typs []types.Type) bool { 13 | if len(typs) > 0 && typs[len(typs)-1] == types.VARIADIC { // last type is variadic, check up to that 14 | if len(args) < len(typs)-2 { // Ignore last 2 since variadic means 0 or more 15 | b.Error(ErrorLevelError, pos, "wrong number of arguments: expected at least %d, got %d", len(typs)-2, len(args)) 16 | return true 17 | } 18 | 19 | hasErr := false 20 | for i, v := range args { 21 | if i < len(typs)-2 { 22 | if !typs[i].Equal(v.Type()) && !types.INVALID.Equal(v.Type()) { 23 | b.Error(ErrorLevelError, v.Pos(), "wrong argument type: expected %s, got %s", typs[i], args[i].Type()) 24 | hasErr = true 25 | } 26 | } else if !typs[len(typs)-2].Equal(v.Type()) && !types.INVALID.Equal(v.Type()) { 27 | b.Error(ErrorLevelError, v.Pos(), "wrong variadic argument type: expected %s, got %s", typs[len(typs)-2], args[i].Type()) 28 | hasErr = true 29 | } 30 | } 31 | 32 | return hasErr 33 | } 34 | 35 | if len(args) != len(typs) { 36 | b.Error(ErrorLevelError, pos, "wrong number of arguments: expected %d, got %d", len(typs), len(args)) 37 | return true 38 | } 39 | 40 | hasErr := false 41 | for i, arg := range args { 42 | if !typs[i].Equal(arg.Type()) && !types.INVALID.Equal(arg.Type()) { 43 | b.Error(ErrorLevelError, arg.Pos(), "wrong argument type: expected %s, got %s", typs[i], arg.Type()) 44 | hasErr = true 45 | } 46 | } 47 | return hasErr 48 | } 49 | 50 | func (b *Builder) FixTypes(args *[]Node, typs []types.Type, pos *tokens.Pos) { 51 | if len(*args) > len(typs) && len(typs) > 0 && typs[len(typs)-1] != types.VARIADIC { 52 | *args = (*args)[:len(typs)] 53 | } 54 | 55 | // Fix existing 56 | for i, v := range *args { 57 | var typ types.Type 58 | if typs[len(typs)-1] == types.VARIADIC { 59 | typ = typs[len(typs)-2] 60 | } else { 61 | typ = typs[i] 62 | } 63 | if !typ.Equal(v.Type()) && !types.INVALID.Equal(v.Type()) { 64 | (*args)[i] = NewTypedNode(typs[i], v.Pos()) 65 | } 66 | } 67 | 68 | if len(typs) > 0 && typs[len(typs)-1] == types.VARIADIC { 69 | if len(*args) < len(typs)-1 { 70 | start := len(*args) - 1 71 | *args = append(*args, make([]Node, len(typs)-len(*args)-1)...) 72 | for i := start; i < len(*args); i++ { 73 | (*args)[i] = NewTypedNode(typs[i], pos) 74 | } 75 | } 76 | return 77 | } 78 | 79 | if len(*args) < len(typs) { 80 | start := len(*args) 81 | *args = append(*args, make([]Node, len(typs)-len(*args))...) 82 | for i := start; i < len(*args); i++ { 83 | (*args)[i] = NewTypedNode(typs[i], pos) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /std/strings.bsp: -------------------------------------------------------------------------------- 1 | [FUNC JOIN [PARAM vals ARRAY{STRING}] [PARAM joiner STRING] [RETURNS STRING] 2 | [DEFINE out ""] 3 | [DEFINE i 0] 4 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR vals]]] 5 | [DEFINE out [CONCAT [VAR out] [INDEX [VAR vals] [VAR i]]]] 6 | [IF [COMPARE [VAR i] != [MATH [LENGTH [VAR vals]] - 1]] 7 | [DEFINE out [CONCAT [VAR out] [VAR joiner]]] 8 | ] 9 | [DEFINE i [MATH [VAR i] + 1]] 10 | ] 11 | [RETURN [VAR out]] 12 | ] 13 | 14 | [FUNC SPLIT [PARAM str STRING] [PARAM sep STRING] [RETURNS ARRAY{STRING}] 15 | [DEFINE i 0] 16 | [DEFINE curr ""] 17 | [DEFINE out [MAKE ARRAY{STRING}]] 18 | [WHILE [COMPARE [LENGTH [VAR sep]] <= [MATH [LENGTH [VAR str]] - [VAR i]]] # Loop until sep is not possible 19 | # Check if the next characters are the seperator 20 | [DEFINE j 0] 21 | [DEFINE issep TRUE] 22 | [WHILE [AND [COMPARE [VAR j] < [LENGTH [VAR sep]]] [VAR issep]] 23 | [IF [COMPARE [INDEX [VAR str] [MATH [VAR i] + [VAR j]]] != [INDEX [VAR sep] [VAR j]]] 24 | [DEFINE issep FALSE] 25 | ] 26 | [DEFINE j [MATH [VAR j] + 1]] 27 | ] 28 | [IF [VAR issep] 29 | # It is sep, skip it and add curr to out 30 | [DEFINE i [MATH [VAR i] + [LENGTH [VAR sep]]]] 31 | [APPEND [VAR out] [VAR curr]] 32 | [DEFINE curr ""] 33 | ] 34 | # Add curr char if not out of bounds 35 | [IF [COMPARE [VAR i] < [LENGTH [VAR str]]] 36 | [DEFINE curr [CONCAT [VAR curr] [STRING [INDEX [VAR str] [VAR i]]]]] 37 | ] 38 | [DEFINE i [MATH [VAR i] + 1]] 39 | ] 40 | 41 | # Add remaining characters 42 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR str]]] 43 | [DEFINE curr [CONCAT [VAR curr] [STRING [INDEX [VAR str] [VAR i]]]]] 44 | [DEFINE i [MATH [VAR i] + 1]] 45 | ] 46 | [IF [COMPARE [VAR curr] != ""] 47 | [APPEND [VAR out] [VAR curr]] 48 | ] 49 | [RETURN [VAR out]] 50 | ] 51 | 52 | [FUNC TOLOWER [PARAM str STRING] [RETURNS STRING] 53 | [DEFINE i 0] 54 | [DEFINE arr [MAKE ARRAY{BYTE}]] 55 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR str]]] 56 | [DEFINE c [INT [INDEX [VAR str] [VAR i]]]] 57 | [IF [AND [COMPARE [INT 'A'] <= [VAR c]] [COMPARE [VAR c] <= [INT 'Z']]] 58 | [DEFINE c [MATH [VAR c] + [MATH [INT 'a'] - [INT 'A']]]] 59 | ] 60 | [APPEND [VAR arr] [BYTE [VAR c]]] 61 | [DEFINE i [MATH [VAR i] + 1]] 62 | ] 63 | [RETURN [STRING [VAR arr]]] 64 | ] 65 | 66 | [FUNC TOUPPER [PARAM str STRING] [RETURNS STRING] 67 | [DEFINE i 0] 68 | [DEFINE arr [MAKE ARRAY{BYTE}]] 69 | [WHILE [COMPARE [VAR i] < [LENGTH [VAR str]]] 70 | [DEFINE c [INT [INDEX [VAR str] [VAR i]]]] 71 | [IF [AND [COMPARE [INT 'a'] <= [VAR c]] [COMPARE [VAR c] <= [INT 'z']]] 72 | [DEFINE c [MATH [VAR c] - [MATH [INT 'a'] - [INT 'A']]]] 73 | ] 74 | [APPEND [VAR arr] [BYTE [VAR c]]] 75 | [DEFINE i [MATH [VAR i] + 1]] 76 | ] 77 | [RETURN [STRING [VAR arr]]] 78 | ] -------------------------------------------------------------------------------- /bsharp-lsp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/tliron/glsp" 5 | protocol "github.com/tliron/glsp/protocol_3_16" 6 | "github.com/tliron/glsp/server" 7 | "github.com/tliron/kutil/logging" 8 | 9 | // Must include a backend implementation. See kutil's logging/ for other options. 10 | _ "github.com/tliron/kutil/logging/simple" 11 | ) 12 | 13 | const lsName = "B#" 14 | 15 | var version string = "0.0.1" 16 | var handler protocol.Handler 17 | 18 | func Ptr[T any](v T) *T { 19 | return &v 20 | } 21 | 22 | func main() { 23 | // This increases logging verbosity (optional) 24 | logging.Configure(1, nil) 25 | 26 | handler = protocol.Handler{ 27 | Initialize: initialize, 28 | Initialized: initialized, 29 | Shutdown: shutdown, 30 | SetTrace: setTrace, 31 | TextDocumentCompletion: textDocumentCompletion, 32 | TextDocumentDidOpen: textDocumentDidOpen, 33 | TextDocumentDidChange: textDocumentDidChange, 34 | TextDocumentDidClose: textDocumentDidClose, 35 | TextDocumentDidSave: textDocumentDidSave, 36 | TextDocumentSemanticTokensFull: semanticTokensFull, 37 | TextDocumentHover: docHover, 38 | TextDocumentDefinition: definitionFn, 39 | } 40 | 41 | server := server.NewServer(&handler, lsName, false) 42 | 43 | server.RunStdio() 44 | } 45 | 46 | func initialize(context *glsp.Context, params *protocol.InitializeParams) (interface{}, error) { 47 | capabilities := handler.CreateServerCapabilities() 48 | capabilities.CompletionProvider = &protocol.CompletionOptions{ 49 | TriggerCharacters: []string{"[", " "}, 50 | } 51 | capabilities.TextDocumentSync = Ptr(protocol.TextDocumentSyncKindFull) 52 | capabilities.SemanticTokensProvider = protocol.SemanticTokensOptions{ 53 | Legend: protocol.SemanticTokensLegend{ 54 | TokenTypes: []string{string(protocol.SemanticTokenTypeType), string(protocol.SemanticTokenTypeVariable), string(protocol.SemanticTokenTypeFunction)}, 55 | TokenModifiers: []string{}, 56 | }, 57 | Range: false, 58 | Full: true, 59 | } 60 | capabilities.HoverProvider = true 61 | capabilities.DefinitionProvider = true 62 | 63 | Root = *params.RootPath 64 | RootURI = *params.RootURI 65 | 66 | return protocol.InitializeResult{ 67 | Capabilities: capabilities, 68 | ServerInfo: &protocol.InitializeResultServerInfo{ 69 | Name: lsName, 70 | Version: &version, 71 | }, 72 | }, nil 73 | } 74 | 75 | func initialized(context *glsp.Context, params *protocol.InitializedParams) error { 76 | return nil 77 | } 78 | 79 | func shutdown(context *glsp.Context) error { 80 | protocol.SetTraceValue(protocol.TraceValueOff) 81 | return nil 82 | } 83 | 84 | func setTrace(context *glsp.Context, params *protocol.SetTraceParams) error { 85 | protocol.SetTraceValue(params.Value) 86 | return nil 87 | } 88 | -------------------------------------------------------------------------------- /ir/vars.go: -------------------------------------------------------------------------------- 1 | package ir 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/tokens" 5 | "github.com/Nv7-Github/bsharp/types" 6 | ) 7 | 8 | type VarNode struct { 9 | ID int 10 | typ types.Type 11 | 12 | name string // Variable name for use in Code() 13 | pos *tokens.Pos 14 | } 15 | 16 | func (v *VarNode) Type() types.Type { return v.typ } 17 | func (v *VarNode) Args() []Node { return []Node{NewConst(types.IDENT, v.pos, v.name)} } 18 | 19 | type DefineNode struct { 20 | NullCall 21 | Var int 22 | Value Node 23 | InScope bool // Check if accessing var outside of function (helps with recursion) 24 | 25 | name string // Variable name for use in Code() 26 | pos *tokens.Pos 27 | } 28 | 29 | func (d *DefineNode) Args() []Node { return []Node{NewConst(types.IDENT, d.pos, d.name), d.Value} } 30 | 31 | func init() { 32 | nodeBuilders["VAR"] = nodeBuilder{ 33 | ArgTypes: []types.Type{types.IDENT}, 34 | Build: func(b *Builder, pos *tokens.Pos, args []Node) (Call, error) { 35 | _, ok := args[0].(*Const) 36 | if !ok { 37 | return NewTypedValue(types.INVALID), nil 38 | } 39 | name := args[0].(*Const).Value.(string) 40 | v, exists := b.Scope.GetVar(name) 41 | if !exists { 42 | b.Error(ErrorLevelError, args[0].Pos(), "undefined variable: %s", name) 43 | return NewTypedValue(types.INVALID), nil 44 | } 45 | va := b.Scope.Variable(v) 46 | if !va.NeedsGlobal && va.ScopeType == ScopeTypeGlobal && b.Scope.HasType(ScopeTypeFunction) { 47 | va.NeedsGlobal = true 48 | } 49 | 50 | return &VarNode{ 51 | ID: v, 52 | typ: va.Type, 53 | name: name, 54 | pos: args[0].Pos(), 55 | }, nil 56 | }, 57 | } 58 | 59 | nodeBuilders["DEFINE"] = nodeBuilder{ 60 | ArgTypes: []types.Type{types.IDENT, types.ALL}, 61 | Build: func(b *Builder, pos *tokens.Pos, args []Node) (Call, error) { 62 | // Get var 63 | name := args[0].(*Const).Value.(string) 64 | id, exists := b.Scope.GetVar(name) 65 | if !exists { 66 | id = b.Scope.AddVariable(name, args[1].Type(), pos) 67 | } else { 68 | v := b.Scope.Variable(id) 69 | if !v.Type.Equal(args[1].Type()) { 70 | // Check if in current scope, if so redefine 71 | _, exists = b.Scope.CurrScopeGetVar(name) 72 | if !exists { 73 | id = b.Scope.AddVariable(name, args[1].Type(), pos) 74 | } else { 75 | return nil, pos.Error("cannot redefine variable %s to type %s", name, args[1].Type()) 76 | } 77 | } 78 | } 79 | _, exists = b.Scope.CurrScopeGetVar(name) 80 | 81 | // check if needs global 82 | v := b.Scope.Variable(id) 83 | if !v.NeedsGlobal && v.ScopeType == ScopeTypeGlobal && b.Scope.HasType(ScopeTypeFunction) { 84 | v.NeedsGlobal = true 85 | } 86 | 87 | return &DefineNode{ 88 | Var: id, 89 | Value: args[1], 90 | name: name, 91 | InScope: exists, 92 | pos: args[0].Pos(), 93 | }, nil 94 | }, 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /bsharp-lsp/goto.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | "github.com/Nv7-Github/bsharp/tokens" 7 | "github.com/tliron/glsp" 8 | protocol "github.com/tliron/glsp/protocol_3_16" 9 | ) 10 | 11 | func definitionFn(context *glsp.Context, params *protocol.DefinitionParams) (interface{}, error) { 12 | // Find token 13 | doc, exists := Documents[params.TextDocument.URI] 14 | if !exists { 15 | return nil, nil 16 | } 17 | id := tokID(params.Position, doc) 18 | if id < 0 { 19 | return nil, nil 20 | } 21 | 22 | // Check if function, get name 23 | var name *string = nil 24 | tok := doc.Tokens.Tokens[id] 25 | if matchPrev(id, doc, []TokenMatcher{TokMatchT(tokens.TokenTypeLBrack)}) { 26 | name = &tok.Value 27 | } else if matchPrev(id, doc, []TokenMatcher{TokMatchTV(tokens.TokenTypeIdent, "FN"), TokMatchT(tokens.TokenTypeLBrack)}) { 28 | name = &tok.Value 29 | } 30 | if name == nil { 31 | // Variable? 32 | if matchPrev(id, doc, []TokenMatcher{TokMatchTV(tokens.TokenTypeIdent, "VAR"), TokMatchT(tokens.TokenTypeLBrack)}) { 33 | // Variable 34 | scope := GetScope(doc.IRCache, tok.Pos) 35 | if scope == nil { 36 | return nil, nil 37 | } 38 | for i := len(scope.Frames) - 1; i >= 0; i-- { 39 | for _, v := range scope.Frames[i].Variables { 40 | if doc.IRCache.Variables[v].Name == tok.Value { 41 | // Get var pos 42 | va := doc.IRCache.Variables[v] 43 | id := tokID(protocol.Position{ 44 | Line: uint32(va.Pos.Line), 45 | Character: uint32(va.Pos.Char), 46 | }, doc) 47 | name := doc.Tokens.Tokens[id+2] 48 | 49 | return protocol.Location{ 50 | URI: filepath.Join(RootURI, name.Pos.File), 51 | Range: protocol.Range{ 52 | Start: protocol.Position{ 53 | Line: uint32(name.Pos.Line), 54 | Character: uint32(name.Pos.Char), 55 | }, 56 | End: protocol.Position{ 57 | Line: uint32(name.Pos.EndLine), 58 | Character: uint32(name.Pos.EndChar), 59 | }, 60 | }, 61 | }, nil 62 | } 63 | } 64 | } 65 | } 66 | return nil, nil 67 | } 68 | 69 | // Function, check if exists and find pos 70 | for _, fn := range doc.IRCache.Funcs { 71 | if fn.Name == *name { 72 | // Get name pos 73 | id := tokID(protocol.Position{ 74 | Line: uint32(fn.Pos().Line), 75 | Character: uint32(fn.Pos().Char), 76 | }, doc) 77 | name := doc.Tokens.Tokens[id+2] 78 | 79 | return protocol.Location{ 80 | URI: filepath.Join(RootURI, fn.Pos().File), 81 | Range: protocol.Range{ 82 | Start: protocol.Position{ 83 | Line: uint32(name.Pos.Line), 84 | Character: uint32(name.Pos.Char), 85 | }, 86 | End: protocol.Position{ 87 | Line: uint32(name.Pos.EndLine), 88 | Character: uint32(name.Pos.EndChar), 89 | }, 90 | }, 91 | }, nil 92 | } 93 | } 94 | 95 | return nil, nil 96 | } 97 | -------------------------------------------------------------------------------- /backends/bstar/bstar.go: -------------------------------------------------------------------------------- 1 | package bstar 2 | 3 | import ( 4 | "encoding/base32" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/Nv7-Github/bsharp/ir" 10 | ) 11 | 12 | var enc = base32.NewEncoding("abcdefghijklmnopqrstuvwxyzABCDEF") 13 | 14 | type BStar struct { 15 | ir *ir.IR 16 | } 17 | 18 | type Node interface { 19 | Code(opts *BStarConfig) string 20 | } 21 | 22 | type BlockNode struct { 23 | Body []Node 24 | DoesPrint bool 25 | } 26 | 27 | type BStarConfig struct { 28 | Seperator string 29 | } 30 | 31 | func (n *BlockNode) Code(opts *BStarConfig) string { 32 | out := &strings.Builder{} 33 | out.WriteByte('[') 34 | for i, n := range n.Body { 35 | if i > 0 { 36 | out.WriteString(opts.Seperator) 37 | } 38 | out.WriteString(n.Code(opts)) 39 | } 40 | out.WriteByte(']') 41 | return out.String() 42 | } 43 | 44 | type ConstNode struct{ any } 45 | 46 | func (c ConstNode) Code(opts *BStarConfig) string { 47 | switch v := c.any.(type) { 48 | case int: 49 | return fmt.Sprintf("%d", v) 50 | 51 | case float64: 52 | return fmt.Sprintf("%f", v) 53 | 54 | case byte: 55 | return fmt.Sprintf("%q", string(v)) 56 | 57 | case bool: 58 | if v { 59 | return "1" 60 | } 61 | return "0" 62 | 63 | case string: 64 | if v[0] != '"' { 65 | for i := 0; i < 10; i++ { // if contains number, base32 encode because numbers not allowed in idents 66 | if strings.Contains(v, strconv.Itoa(i)) { 67 | return formatName(v) 68 | } 69 | } 70 | } 71 | } 72 | return fmt.Sprintf("%v", c.any) 73 | } 74 | 75 | func constNode(v any) Node { 76 | return ConstNode{v} 77 | } 78 | 79 | func blockNode(doesPrint bool, body ...Node) Node { 80 | return &BlockNode{Body: body, DoesPrint: doesPrint} 81 | } 82 | 83 | func NewBStar(i *ir.IR) *BStar { 84 | return &BStar{i} 85 | } 86 | 87 | func (b *BStar) Build() (Node, error) { 88 | // Build funcs 89 | funcs := make([]Node, 0, len(b.ir.Funcs)) 90 | for _, fn := range b.ir.Funcs { 91 | v, err := b.buildFn(fn) 92 | if err != nil { 93 | return nil, err 94 | } 95 | funcs = append(funcs, v) 96 | } 97 | 98 | out, err := b.buildNodes(b.ir.Body) 99 | if err != nil { 100 | return nil, err 101 | } 102 | body := []Node{constNode("BLOCK")} 103 | body = append(body, b.mapSetFunc(), b.mapGetFunc()) 104 | body = append(body, funcs...) 105 | body = append(body, b.buildFnMap()) 106 | body = append(body, out...) 107 | body = append(body, b.noPrintNode()) 108 | return blockNode(false, body...), nil 109 | } 110 | 111 | func (b *BStar) noPrint(node Node) Node { 112 | return blockNode(false, constNode("DEFINE"), constNode("noprint"), node) 113 | } 114 | 115 | func (b *BStar) noPrintNode() Node { 116 | return constNode(`""`) 117 | } 118 | 119 | func formatName(name string) string { 120 | return strings.TrimRight(enc.EncodeToString([]byte(name)), "=") 121 | } 122 | -------------------------------------------------------------------------------- /bot/db/save.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | func (d *Data) SaveProgram(p *Program) error { 12 | d.Lock() 13 | defer d.Unlock() 14 | 15 | var f *os.File 16 | prog, exists := d.Programs[p.ID] 17 | if !exists { 18 | var err error 19 | f, err = os.Create(filepath.Join(d.path, "programs", p.ID+".json")) 20 | if err != nil { 21 | return err 22 | } 23 | d.programFiles[p.ID] = f 24 | d.names[p.Name] = p.ID 25 | } else { 26 | f = d.programFiles[p.ID] 27 | _, err := f.Seek(0, 0) 28 | if err != nil { 29 | return err 30 | } 31 | err = f.Truncate(0) 32 | if err != nil { 33 | return err 34 | } 35 | if prog.Name != p.Name { // Changed name? 36 | delete(d.names, prog.Name) 37 | d.names[p.Name] = p.ID 38 | } 39 | } 40 | 41 | // Save 42 | d.Programs[p.ID] = p 43 | enc := json.NewEncoder(f) 44 | return enc.Encode(p) 45 | } 46 | 47 | func (d *Data) SaveSource(id, source string) error { 48 | d.Lock() 49 | defer d.Unlock() 50 | 51 | var f *os.File 52 | _, exists := d.source[id] 53 | if !exists { 54 | var err error 55 | f, err = os.Create(filepath.Join(d.path, "source", id+".bsp")) 56 | if err != nil { 57 | return err 58 | } 59 | d.sourceFiles[id] = f 60 | } else { 61 | f = d.sourceFiles[id] 62 | _, err := f.Seek(0, 0) 63 | if err != nil { 64 | return err 65 | } 66 | err = f.Truncate(0) 67 | if err != nil { 68 | return err 69 | } 70 | } 71 | 72 | // Save 73 | d.source[id] = source 74 | _, err := f.WriteString(source) 75 | return err 76 | } 77 | 78 | func (d *Data) DataSet(id string, key, val string) error { 79 | d.Lock() 80 | defer d.Unlock() 81 | 82 | if len(key) > MaxKeySize { 83 | return errors.New("db: key exceeds max size (256)") 84 | } 85 | 86 | // Get data 87 | v, exists := d.data[id] 88 | if !exists { 89 | v = make(map[string]string) 90 | d.data[id] = v 91 | } 92 | 93 | // Get file 94 | f, exists := d.dataFiles[id] 95 | if !exists { 96 | var err error 97 | f, err = os.Create(filepath.Join(d.path, "data", id+".json")) 98 | if err != nil { 99 | return err 100 | } 101 | d.dataFiles[id] = f 102 | } 103 | 104 | // Calc size 105 | newsize := d.datasize 106 | e, exists := v[key] 107 | if !exists { 108 | newsize += len(key) + len(val) 109 | } else { 110 | newsize -= len(e) 111 | newsize += len(val) 112 | } 113 | 114 | if newsize > MaxSize { 115 | return fmt.Errorf("db: max size (1MB) exceeded") 116 | } 117 | 118 | // Write 119 | op, err := json.Marshal(dataOp{ 120 | Key: key, 121 | Value: val, 122 | }) 123 | if err != nil { 124 | return err 125 | } 126 | _, err = f.WriteString(string(op) + "\n") 127 | if err != nil { 128 | return err 129 | } 130 | 131 | // Save 132 | v[key] = val 133 | 134 | return nil 135 | } 136 | -------------------------------------------------------------------------------- /old/ssa/constrm/eval.go: -------------------------------------------------------------------------------- 1 | package constrm 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/Nv7-Github/bsharp/ir" 7 | "github.com/Nv7-Github/bsharp/old/ssa" 8 | "github.com/Nv7-Github/bsharp/types" 9 | ) 10 | 11 | type compOpValue interface { 12 | int | float64 | string 13 | } 14 | 15 | func compOp[T compOpValue](lhs T, rhs T, op ir.CompareOperation) *bool { 16 | var out bool 17 | switch op { 18 | case ir.CompareOperationEqual: 19 | out = lhs == rhs 20 | 21 | case ir.CompareOperationNotEqual: 22 | out = lhs != rhs 23 | 24 | case ir.CompareOperationGreater: 25 | out = lhs > rhs 26 | 27 | case ir.CompareOperationGreaterEqual: 28 | out = lhs >= rhs 29 | 30 | case ir.CompareOperationLess: 31 | out = lhs < rhs 32 | 33 | case ir.CompareOperationLessEqual: 34 | out = lhs <= rhs 35 | 36 | default: 37 | return nil 38 | } 39 | 40 | return &out 41 | } 42 | 43 | type mathOpValue interface { 44 | int | float64 45 | } 46 | 47 | func mathOp[T mathOpValue](lhs T, rhs T, op ir.MathOperation) *T { 48 | var out T 49 | switch op { 50 | case ir.MathOperationAdd: 51 | out = lhs + rhs 52 | 53 | case ir.MathOperationSub: 54 | out = lhs - rhs 55 | 56 | case ir.MathOperationMul: 57 | out = lhs * rhs 58 | 59 | case ir.MathOperationDiv: 60 | out = lhs / rhs 61 | 62 | case ir.MathOperationPow: 63 | switch any(lhs).(type) { 64 | case int: 65 | out = T(math.Pow(float64(lhs), float64(rhs))) 66 | 67 | case float64: 68 | out = T(math.Pow(float64(lhs), float64(rhs))) 69 | } 70 | 71 | case ir.MathOperationMod: 72 | switch any(lhs).(type) { 73 | case int: 74 | out = T(int(lhs) % int(rhs)) 75 | 76 | case float64: 77 | out = T(math.Mod(float64(lhs), float64(rhs))) 78 | } 79 | 80 | default: 81 | return nil 82 | } 83 | 84 | return &out 85 | } 86 | 87 | func evalMath(blk *ssa.Block, i *ssa.Math) *ssa.Const { 88 | switch i.Type().BasicType() { 89 | case types.INT: 90 | return &ssa.Const{ 91 | Value: mathOp(cnst(blk, i.Lhs).(int), cnst(blk, i.Rhs).(int), i.Op), 92 | Typ: types.INT, 93 | } 94 | 95 | case types.FLOAT: 96 | return &ssa.Const{ 97 | Value: mathOp(cnst(blk, i.Lhs).(float64), cnst(blk, i.Rhs).(float64), i.Op), 98 | Typ: types.FLOAT, 99 | } 100 | } 101 | 102 | return nil 103 | } 104 | 105 | func evalComp(blk *ssa.Block, i *ssa.Compare) *ssa.Const { 106 | l := blk.Instructions[i.Lhs] 107 | var out *bool 108 | switch l.Type().BasicType() { 109 | case types.INT: 110 | out = compOp(cnst(blk, i.Lhs).(int), cnst(blk, i.Rhs).(int), i.Op) 111 | case types.FLOAT: 112 | out = compOp(cnst(blk, i.Lhs).(float64), cnst(blk, i.Rhs).(float64), i.Op) 113 | 114 | case types.STRING: 115 | out = compOp(cnst(blk, i.Lhs).(string), cnst(blk, i.Rhs).(string), i.Op) 116 | } 117 | if out != nil { 118 | return &ssa.Const{ 119 | Value: *out, 120 | Typ: types.BOOL, 121 | } 122 | } 123 | 124 | return nil 125 | } 126 | -------------------------------------------------------------------------------- /backends/cgen/types.go: -------------------------------------------------------------------------------- 1 | package cgen 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/Nv7-Github/bsharp/types" 8 | ) 9 | 10 | const Namespace = "bsharp__" 11 | 12 | func (c *CGen) CType(typ types.Type, fn ...bool) string { 13 | switch typ.BasicType() { 14 | case types.INT: 15 | return "long" 16 | 17 | case types.FLOAT: 18 | return "double" 19 | 20 | case types.BOOL: 21 | return "bool" 22 | 23 | case types.BYTE: 24 | return "char" 25 | 26 | case types.STRING: 27 | return "string*" 28 | 29 | case types.FUNCTION: 30 | name := typName(typ) + "_typ" 31 | _, exists := c.addedFns[name] 32 | if exists { 33 | return name 34 | } 35 | 36 | t := typ.(*types.FuncType) 37 | out := &strings.Builder{} 38 | out.WriteString("typedef ") 39 | out.WriteString(c.CType(t.RetType)) 40 | out.WriteString(" (*") 41 | out.WriteString(name) 42 | out.WriteString(")(") 43 | for i, arg := range t.ParTypes { 44 | if i != 0 { 45 | out.WriteString(", ") 46 | } 47 | out.WriteString(c.CType(arg)) 48 | } 49 | out.WriteString(");\n") 50 | c.globaltyps.WriteString(out.String()) 51 | c.addedFns[name] = struct{}{} 52 | 53 | return name 54 | 55 | case types.ARRAY: 56 | return "array*" 57 | 58 | case types.MAP: 59 | return "map*" 60 | 61 | case types.NULL: 62 | if len(fn) > 0 { 63 | return "void" 64 | } 65 | return "void*" 66 | 67 | case types.STRUCT: 68 | name := typName(typ) 69 | _, exists := c.addedFns[name] 70 | if exists { 71 | return "struct " + name + "*" 72 | } 73 | sTyp := &strings.Builder{} 74 | fmt.Fprintf(sTyp, "struct %s {\n", name) 75 | for i, field := range typ.(*types.StructType).Fields { 76 | fmt.Fprintf(sTyp, "%s%s f%d;\n", c.Config.Tab, c.CType(field.Type), i) 77 | } 78 | fmt.Fprintf(sTyp, "%sint refs;\n", c.Config.Tab) 79 | sTyp.WriteString("};\n\n") 80 | c.globaltyps.WriteString(sTyp.String()) 81 | 82 | // Free function 83 | out := &strings.Builder{} 84 | fmt.Fprintf(out, "void %s_free(struct %s* val) {;\n", name, name) 85 | fmt.Fprintf(out, "%sif (val == NULL) {\n%s%sreturn;\n%s}\n", c.Config.Tab, c.Config.Tab, c.Config.Tab, c.Config.Tab) 86 | fmt.Fprintf(out, "%sval->refs--;\n", c.Config.Tab) 87 | fmt.Fprintf(out, "%sif(val->refs == 0) {\n", c.Config.Tab) 88 | for i, field := range typ.(*types.StructType).Fields { 89 | if isDynamic(field.Type) { 90 | fmt.Fprintf(out, "%s%s%s;\n", c.Config.Tab, c.Config.Tab, c.FreeCode(fmt.Sprintf("val->f%d", i), field.Type)) 91 | } 92 | } 93 | fmt.Fprintf(out, "%s%sfree(val);\n", c.Config.Tab, c.Config.Tab) 94 | fmt.Fprintf(out, "%s}\n", c.Config.Tab) 95 | out.WriteString("}\n\n") 96 | 97 | // Return 98 | c.addedFns[name] = struct{}{} 99 | c.globalfns.WriteString(out.String()) 100 | return "struct " + name + "*" 101 | 102 | case types.ANY: 103 | return "any*" 104 | } 105 | 106 | panic("invalid type") 107 | } 108 | -------------------------------------------------------------------------------- /examples/bot/wordle.bsp: -------------------------------------------------------------------------------- 1 | [IMPORT "math.bsp"] 2 | [IMPORT "strings.bsp"] 3 | 4 | # Get allowed words 5 | [DEFINE start [TIME MILLI]] 6 | [DEFINE vals [DB GET "allowed"]] 7 | [DEFINE allowed [MAKE MAP{STRING,BOOL}]] 8 | [DEFINE i 0] 9 | [WHILE [COMPARE [VAR i] < [MATH [LENGTH [VAR vals]] / 6]] 10 | [SET [VAR allowed] [SLICE [VAR vals] [MATH [VAR i] * 6] [MATH [MATH [VAR i] * 6] + 5]] TRUE] 11 | [DEFINE i [MATH [VAR i] + 1]] 12 | ] 13 | [DEFINE vals [DB GET "possible"]] 14 | [DEFINE i 0] 15 | [WHILE [COMPARE [VAR i] < [MATH [LENGTH [VAR vals]] / 6]] 16 | [SET [VAR allowed] [SLICE [VAR vals] [MATH [VAR i] * 6] [MATH [MATH [VAR i] * 6] + 5]] TRUE] 17 | [DEFINE i [MATH [VAR i] + 1]] 18 | ] 19 | 20 | # Colors 21 | [DEFINE C ""] # Reset 22 | [DEFINE Y ""] # Yellow 23 | [DEFINE G ""] # Green 24 | 25 | # Setup vars 26 | [DEFINE possible [DB GET "possible"]] 27 | [DEFINE ind [RANDINT 0 [MATH [MATH [LENGTH [VAR possible]] / 6] - 1]]] 28 | [DEFINE currword [SLICE [VAR possible] [MATH [VAR ind] * 6] [MATH [MATH [VAR ind] * 6] + 5]]] 29 | [DEFINE guesses [MAKE ARRAY{STRING}]] 30 | 31 | [PRINT [CONCAT "Loaded in " [STRING [MATH [TIME MILLI] - [VAR start]]] "ms."]] 32 | 33 | # Guess 34 | [DEFINE running TRUE] 35 | [DEFINE started FALSE] 36 | [FUNC DOGUESS 37 | [IF [NOT [VAR started]] 38 | [PRINT "===== Guesses =====```\n```ansi"] 39 | [DEFINE started TRUE] 40 | ] 41 | 42 | [DEFINE guess [INPUT "Guess a word"]] 43 | 44 | [IF [NOT [EXISTS [VAR allowed] [VAR guess]]] 45 | [RETURN [PRINT "That's not a word!"]] 46 | ] 47 | 48 | # Save guess 49 | [APPEND [VAR guesses] [VAR guess]] 50 | 51 | # Print guesses 52 | [DEFINE line ""] 53 | [DEFINE j 0] 54 | [WHILE [COMPARE [VAR j] < [LENGTH [VAR guess]]] 55 | [DEFINE char [INDEX [VAR guess] [VAR j]]] 56 | [DEFINE color [VAR C]] 57 | # Check if char in currword, if so make it yellow 58 | [DEFINE k 0] 59 | [WHILE [COMPARE [VAR k] < [LENGTH [VAR currword]]] 60 | [IF [COMPARE [INDEX [VAR currword] [VAR k]] == [VAR char]] 61 | [DEFINE color [VAR Y]] 62 | ] 63 | [DEFINE k [MATH [VAR k] + 1]] 64 | ] 65 | # Check if char is correct 66 | [IF [COMPARE [INDEX [VAR currword] [VAR j]] == [VAR char]] 67 | [DEFINE color [VAR G]] 68 | ] 69 | 70 | # Make line 71 | [DEFINE line [CONCAT [VAR line] [VAR color] "[" [STRING [VAR char]] "]"]] 72 | 73 | [DEFINE j [MATH [VAR j] + 1]] 74 | ] 75 | [PRINT [VAR line]] 76 | 77 | # Check if won 78 | [DEFINE guess [INDEX [VAR guesses] [MATH [LENGTH [VAR guesses]] - 1]]] 79 | [IF [COMPARE [VAR guess] == [VAR currword]] 80 | [DEFINE running FALSE] 81 | [RETURN [PRINT "```\n```You got the word!"]] 82 | ] 83 | 84 | # If done, then reset 85 | [IF [COMPARE [LENGTH [VAR guesses]] == 6] 86 | [DEFINE running FALSE] 87 | [RETURN [PRINT "```\n```Maybe you'll get it next time?"]] 88 | ] 89 | 90 | # Wait for input, then continue 91 | ] 92 | 93 | [WHILE [VAR running] 94 | [BUTTONS [ARRAY [ARRAY [COLOR [BUTTON "Continue" "cont"] 4]]]] 95 | [DOGUESS] 96 | ] 97 | -------------------------------------------------------------------------------- /ir/ir.go: -------------------------------------------------------------------------------- 1 | package ir 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Nv7-Github/bsharp/tokens" 7 | "github.com/Nv7-Github/bsharp/types" 8 | ) 9 | 10 | type Node interface { 11 | Type() types.Type 12 | Pos() *tokens.Pos 13 | } 14 | 15 | type Call interface { 16 | Type() types.Type 17 | Args() []Node 18 | } 19 | 20 | type Block interface{} // TODO: Have something in this to make sure its a block 21 | 22 | type Param struct { 23 | ID int 24 | Name string 25 | Type types.Type 26 | Pos *tokens.Pos 27 | } 28 | 29 | type Function struct { 30 | Name string 31 | Params []*Param 32 | RetType types.Type 33 | Body []Node 34 | pos *tokens.Pos 35 | Scope *ScopeInfo 36 | } 37 | 38 | func (f *Function) Pos() *tokens.Pos { return f.pos } 39 | 40 | type empty struct{} 41 | 42 | type ErrorLevel int 43 | 44 | const ( 45 | ErrorLevelError ErrorLevel = iota 46 | ErrorLevelWarning // TODO: Use this 47 | ) 48 | 49 | type Error struct { 50 | Level ErrorLevel 51 | Pos *tokens.Pos 52 | Message string 53 | } 54 | 55 | type Builder struct { 56 | Scope *Scope 57 | Funcs map[string]*Function 58 | Body []Node 59 | imported map[string]empty 60 | currFn string 61 | extensions map[string]*Extension 62 | typeNames map[string]types.Type 63 | consts map[string]*Const 64 | Errors []*Error 65 | } 66 | 67 | func (b *Builder) AddExtension(e *Extension) { 68 | b.extensions[e.Name] = e 69 | } 70 | 71 | func (b *Builder) Error(level ErrorLevel, pos *tokens.Pos, format string, args ...interface{}) { 72 | b.Errors = append(b.Errors, &Error{Level: level, Pos: pos, Message: fmt.Sprintf(format, args...)}) 73 | } 74 | 75 | type IR struct { 76 | Funcs map[string]*Function 77 | Variables []*Variable 78 | Body []Node 79 | GlobalScope *ScopeInfo 80 | } 81 | 82 | func NewBuilder() *Builder { 83 | return &Builder{ 84 | Scope: NewScope(), 85 | Funcs: make(map[string]*Function), 86 | imported: make(map[string]empty), 87 | extensions: make(map[string]*Extension), 88 | typeNames: make(map[string]types.Type), 89 | consts: make(map[string]*Const), 90 | } 91 | } 92 | 93 | func (b *Builder) IR() *IR { 94 | return &IR{ 95 | Funcs: b.Funcs, 96 | Variables: b.Scope.Variables, 97 | Body: b.Body, 98 | GlobalScope: b.Scope.CurrScopeInfo(), 99 | } 100 | } 101 | 102 | type TypedValue struct{ typ types.Type } 103 | 104 | func (t *TypedValue) Type() types.Type { return t.typ } 105 | 106 | func (t *TypedValue) Args() []Node { return []Node{} } 107 | 108 | func NewTypedValue(typ types.Type) Call { return &TypedValue{typ: typ} } 109 | 110 | type TypedNode struct { 111 | typ types.Type 112 | pos *tokens.Pos 113 | } 114 | 115 | func (t *TypedNode) Type() types.Type { return t.typ } 116 | 117 | func (t *TypedNode) Pos() *tokens.Pos { return t.pos } 118 | 119 | func NewTypedNode(typ types.Type, pos *tokens.Pos) Node { 120 | return &TypedNode{typ: typ, pos: pos} 121 | } 122 | -------------------------------------------------------------------------------- /bot/db/read.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | func (d *Data) GetProgram(id string) (*Program, Resp) { 10 | d.RLock() 11 | defer d.RUnlock() 12 | 13 | p, exists := d.Programs[id] 14 | if !exists { 15 | return nil, Resp{Msg: fmt.Sprintf("Program `%s` doesn't exist!", id), Suc: false} 16 | } 17 | return p, Resp{Msg: "", Suc: true} 18 | } 19 | 20 | var validIDChars = "abcdefghijklmnopqrstuvwxyz_-0123456789" 21 | 22 | const maxNameLength = 128 23 | 24 | func (d *Data) NewProgram(id, name, creator string) (*Program, Resp) { 25 | // Validate id 26 | if id[0] == '_' { 27 | return nil, Resp{Msg: "IDs can't start with underscores!", Suc: false} 28 | } 29 | for _, char := range id { 30 | if !strings.Contains(validIDChars, string(char)) { 31 | return nil, Resp{Msg: fmt.Sprintf("Character \"%s\" isn't allowed in program IDs!", string(char)), Suc: false} 32 | } 33 | } 34 | if len([]rune(name)) > maxNameLength { 35 | return nil, Resp{Msg: "Program name must be under 128 characters!", Suc: false} 36 | } 37 | // Check if already exists 38 | _, rsp := d.GetProgram(id) 39 | if rsp.Suc { 40 | return nil, Resp{Msg: fmt.Sprintf("Tag with ID `%s` already exists!", id), Suc: false} 41 | } 42 | _, rsp = d.GetProgramByName(id) 43 | if rsp.Suc { 44 | return nil, Resp{Msg: fmt.Sprintf("Tag with name **%s** already exists!", name), Suc: false} 45 | } 46 | return &Program{ 47 | ID: id, 48 | Name: name, 49 | Creator: creator, 50 | Uses: 0, 51 | Created: time.Now(), 52 | LastUsed: time.Now(), 53 | Description: "None", 54 | Image: "", 55 | }, Resp{Msg: "", Suc: true} 56 | } 57 | 58 | func (d *Data) GetProgramByName(name string) (string, Resp) { 59 | d.RLock() 60 | defer d.RUnlock() 61 | 62 | id, exists := d.names[name] 63 | if !exists { 64 | return "", Resp{Msg: fmt.Sprintf("Program **%s** doesn't exist!", name), Suc: false} 65 | } 66 | return id, Resp{Msg: "", Suc: true} 67 | } 68 | 69 | func (d *Data) GetSource(id string) (string, Resp) { 70 | d.RLock() 71 | defer d.RUnlock() 72 | 73 | src, exists := d.source[id] 74 | if !exists { 75 | return "", Resp{Msg: fmt.Sprintf("Source for `%s` doesn't exist!", id), Suc: false} 76 | } 77 | return src, Resp{Msg: "", Suc: true} 78 | } 79 | 80 | func (d *Data) DataGet(id string, key string) (string, Resp) { 81 | d.RLock() 82 | defer d.RUnlock() 83 | 84 | v, exists := d.data[id] 85 | if !exists { 86 | return "", Resp{Msg: fmt.Sprintf("Key **%s** doesn't exist!", key), Suc: false} 87 | } 88 | val, exists := v[key] 89 | if !exists { 90 | return "", Resp{Msg: fmt.Sprintf("Key **%s** doesn't exist!", key), Suc: false} 91 | } 92 | return val, Resp{Msg: "", Suc: true} 93 | } 94 | 95 | func (d *Data) DataExists(id string, key string) bool { 96 | d.RLock() 97 | defer d.RUnlock() 98 | 99 | v, exists := d.data[id] 100 | if !exists { 101 | return false 102 | } 103 | _, exists = v[key] 104 | return exists 105 | } 106 | -------------------------------------------------------------------------------- /backends/cgen/stmts.go: -------------------------------------------------------------------------------- 1 | package cgen 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/Nv7-Github/bsharp/ir" 7 | ) 8 | 9 | func (cg *CGen) AddNode(node ir.Node) (*Code, error) { 10 | if cg.isReturn { 11 | cg.isReturn = false 12 | } 13 | switch n := node.(type) { 14 | case *ir.CallNode: 15 | switch c := n.Call.(type) { 16 | case *ir.PrintNode: 17 | return cg.addPrint(c) 18 | 19 | case *ir.FnCallNode: 20 | return cg.addFnCall(c) 21 | 22 | case *ir.FnNode: 23 | return cg.addFn(c), nil 24 | 25 | case *ir.DefineNode: 26 | return cg.addDefine(c) 27 | 28 | case *ir.VarNode: 29 | return &Code{ 30 | Value: Namespace + cg.ir.Variables[c.ID].Name + strconv.Itoa(c.ID), 31 | }, nil 32 | 33 | case *ir.ReturnNode: 34 | return cg.addReturn(c) 35 | 36 | case *ir.MathNode: 37 | return cg.addMath(c) 38 | 39 | case *ir.CastNode: 40 | return cg.addCast(c) 41 | 42 | case *ir.CompareNode: 43 | return cg.addCompare(c) 44 | 45 | case *ir.TimeNode: 46 | return cg.addTime(c), nil 47 | 48 | case *ir.ConcatNode: 49 | return cg.addConcat(c) 50 | 51 | case *ir.ArrayNode: 52 | return cg.addArray(c) 53 | 54 | case *ir.IndexNode: 55 | return cg.addIndex(n.Pos(), c) 56 | 57 | case *ir.AppendNode: 58 | return cg.addAppend(c) 59 | 60 | case *ir.MakeNode: 61 | return cg.addMake(c) 62 | 63 | case *ir.SetNode: 64 | return cg.addSet(c) 65 | 66 | case *ir.GetNode: 67 | return cg.addGet(c) 68 | 69 | case *ir.LengthNode: 70 | return cg.addLength(c) 71 | 72 | case *ir.KeysNode: 73 | return cg.addKeys(c) 74 | 75 | case *ir.ExistsNode: 76 | return cg.addExists(c) 77 | 78 | case *ir.LogicalOpNode: 79 | return cg.addLogicalOp(c) 80 | 81 | case *ir.SliceNode: 82 | return cg.addSlice(c) 83 | 84 | case *ir.SetIndexNode: 85 | return cg.addSetIndex(n.Pos(), c) 86 | 87 | case *ir.GetStructNode: 88 | return cg.addGetStruct(c) 89 | 90 | case *ir.SetStructNode: 91 | return cg.addSetStruct(c) 92 | 93 | case *ir.CanCastNode: 94 | return cg.addCanCast(c) 95 | 96 | case *ir.PanicNode: 97 | return cg.addPanic(c, n.Pos()) 98 | 99 | default: 100 | return nil, n.Pos().Error("unknown call node: %T", c) 101 | } 102 | 103 | case *ir.BlockNode: 104 | switch b := n.Block.(type) { 105 | case *ir.IfNode: 106 | return cg.addIf(b) 107 | 108 | case *ir.WhileNode: 109 | return cg.addWhile(b) 110 | 111 | case *ir.SwitchNode: 112 | return cg.addSwitch(b) 113 | 114 | default: 115 | return nil, n.Pos().Error("unknown block node: %T", b) 116 | } 117 | 118 | case *ir.Const: 119 | return cg.addConst(n), nil 120 | 121 | case *ir.FnCallNode: 122 | return cg.addFnCall(n) 123 | 124 | case *ir.CastNode: 125 | return cg.addCast(n) 126 | 127 | case *ir.ExtensionCall: 128 | switch n.Name { 129 | case "INPUT": 130 | return cg.addInput(n) 131 | 132 | default: 133 | return nil, n.Pos().Error("unknown extension call: %s", n.Name) 134 | } 135 | 136 | default: 137 | return nil, n.Pos().Error("unknown node: %T", node) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /backends/bsp/nodes.go: -------------------------------------------------------------------------------- 1 | package bsp 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/Nv7-Github/bsharp/ir" 9 | "github.com/Nv7-Github/bsharp/types" 10 | ) 11 | 12 | func (b *BSP) addFnCall(fn *ir.FnCallNode) (string, error) { 13 | name, err := b.buildNode(fn.Fn) 14 | if err != nil { 15 | return "", err 16 | } 17 | 18 | out := &strings.Builder{} 19 | fmt.Fprintf(out, "[CALL%s%s", b.Config.Seperator, name) 20 | for _, arg := range fn.Params { 21 | v, err := b.buildNode(arg) 22 | if err != nil { 23 | return "", err 24 | } 25 | out.WriteString(b.Config.Seperator) 26 | out.WriteString(v) 27 | } 28 | out.WriteRune(']') 29 | return out.String(), nil 30 | } 31 | 32 | func (b *BSP) addCall(name string, val ir.Call) (string, error) { 33 | out := &strings.Builder{} 34 | fmt.Fprintf(out, "[%s", name) 35 | for _, arg := range val.Args() { 36 | v, err := b.buildNode(arg) 37 | if err != nil { 38 | return "", err 39 | } 40 | out.WriteString(b.Config.Seperator) 41 | out.WriteString(v) 42 | } 43 | out.WriteRune(']') 44 | return out.String(), nil 45 | } 46 | 47 | func (b *BSP) addConst(v *ir.Const) (string, error) { 48 | switch v.Type() { 49 | case types.INT: 50 | return strconv.Itoa(v.Value.(int)), nil 51 | 52 | case types.BYTE: 53 | switch v.Value.(byte) { 54 | case '\n': 55 | return `'\n'`, nil 56 | 57 | case '\'': 58 | return `'\''`, nil 59 | 60 | case '\\': 61 | return `'\\'`, nil 62 | 63 | case '\t': 64 | return `'\t'`, nil 65 | 66 | default: 67 | return "'" + string(v.Value.(byte)) + "'", nil 68 | } 69 | 70 | case types.FLOAT: 71 | v := strconv.FormatFloat(v.Value.(float64), 'f', -1, 64) 72 | if !strings.Contains(v, ".") { 73 | v += ".0" 74 | } 75 | return v, nil 76 | 77 | case types.STRING: 78 | return fmt.Sprintf("%q", v.Value.(string)), nil 79 | 80 | case types.IDENT: 81 | return fmt.Sprintf("%s", v.Value.(string)), nil 82 | 83 | case types.BOOL: 84 | if v.Value.(bool) { 85 | return "TRUE", nil 86 | } 87 | return "FALSE", nil 88 | 89 | case types.NULL: 90 | return "NULL", nil 91 | 92 | default: 93 | return "", v.Pos().Error("unknown const type: %s", v.Type()) 94 | } 95 | } 96 | 97 | func (b *BSP) addCast(n *ir.CastNode) (string, error) { 98 | if types.ANY.Equal(n.Value.Type()) { 99 | return b.addCall("CAST", n) 100 | } 101 | 102 | v, err := b.buildNode(n.Value) 103 | if err != nil { 104 | return "", err 105 | } 106 | 107 | switch n.Type().BasicType() { 108 | case types.ANY: 109 | return fmt.Sprintf("[ANY%s%s]", b.Config.Seperator, v), nil 110 | 111 | case types.STRING: 112 | return fmt.Sprintf("[STRING%s%s]", b.Config.Seperator, v), nil 113 | 114 | case types.FLOAT: 115 | return fmt.Sprintf("[FLOAT%s%s]", b.Config.Seperator, v), nil 116 | 117 | case types.INT: 118 | return fmt.Sprintf("[INT%s%s]", b.Config.Seperator, v), nil 119 | 120 | case types.BYTE: 121 | return fmt.Sprintf("[BYTE%s%s]", b.Config.Seperator, v), nil 122 | } 123 | 124 | return "", n.Pos().Error("cannot cast from %s to %s", n.Value.Type(), n.Type()) 125 | } 126 | -------------------------------------------------------------------------------- /ir/scope.go: -------------------------------------------------------------------------------- 1 | package ir 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/tokens" 5 | "github.com/Nv7-Github/bsharp/types" 6 | ) 7 | 8 | type ScopeType int 9 | 10 | const ( 11 | ScopeTypeGlobal ScopeType = iota 12 | ScopeTypeFunction 13 | ScopeTypeIf 14 | ScopeTypeWhile 15 | ScopeTypeSwitch 16 | ScopeTypeCase 17 | ) 18 | 19 | type Variable struct { 20 | Type types.Type 21 | Name string 22 | ID int 23 | Pos *tokens.Pos 24 | ScopeType ScopeType 25 | NeedsGlobal bool // Useful for backends for which global code is placed inside a function, can allow backends to reduce global variable usage 26 | } 27 | 28 | type scope struct { 29 | Type ScopeType 30 | Variables map[string]int 31 | } 32 | 33 | type Scope struct { 34 | scopes []scope 35 | Variables []*Variable 36 | } 37 | 38 | type ScopeInfo struct { 39 | Frames []ScopeFrame // Top frame is last 40 | } 41 | 42 | type ScopeFrame struct { 43 | Type ScopeType 44 | Variables []int 45 | } 46 | 47 | func (s *Scope) Push(typ ScopeType) { 48 | s.scopes = append(s.scopes, scope{ 49 | Variables: make(map[string]int), 50 | Type: typ, 51 | }) 52 | } 53 | 54 | func (s *Scope) Pop() { 55 | s.scopes = s.scopes[:len(s.scopes)-1] 56 | } 57 | 58 | func (s *Scope) HasType(typ ScopeType) bool { 59 | for _, scope := range s.scopes { 60 | if scope.Type == typ { 61 | return true 62 | } 63 | } 64 | return false 65 | } 66 | 67 | func (s *Scope) CurrScopeInfo() *ScopeInfo { 68 | frames := make([]ScopeFrame, len(s.scopes)) 69 | for i, sc := range s.scopes { 70 | vars := make([]int, len(sc.Variables)) 71 | j := 0 72 | for _, v := range sc.Variables { 73 | vars[j] = v 74 | j++ 75 | } 76 | frames[i] = ScopeFrame{ 77 | Type: sc.Type, 78 | Variables: vars, 79 | } 80 | } 81 | return &ScopeInfo{ 82 | Frames: frames, 83 | } 84 | } 85 | 86 | func (s *Scope) CurrType() ScopeType { 87 | return s.scopes[len(s.scopes)-1].Type 88 | } 89 | 90 | func (s *Scope) GetVar(name string) (int, bool) { 91 | out := 0 92 | existsOut := false 93 | for _, scope := range s.scopes { 94 | v, exists := scope.Variables[name] 95 | if exists { 96 | out = v 97 | existsOut = true 98 | } 99 | } 100 | return out, existsOut 101 | } 102 | 103 | func (s *Scope) CurrScopeGetVar(name string) (int, bool) { 104 | v, exists := s.scopes[len(s.scopes)-1].Variables[name] 105 | return v, exists 106 | } 107 | 108 | func (s *Scope) Variable(id int) *Variable { 109 | return s.Variables[id] 110 | } 111 | 112 | func (s *Scope) AddVariable(name string, typ types.Type, pos *tokens.Pos) int { 113 | v := &Variable{ 114 | Name: name, 115 | Type: typ, 116 | ID: len(s.Variables), 117 | Pos: pos, 118 | ScopeType: s.CurrType(), 119 | } 120 | s.Variables = append(s.Variables, v) 121 | s.scopes[len(s.scopes)-1].Variables[name] = v.ID 122 | return v.ID 123 | } 124 | 125 | func NewScope() *Scope { 126 | s := &Scope{ 127 | Variables: make([]*Variable, 0), 128 | scopes: make([]scope, 0), 129 | } 130 | s.Push(ScopeTypeGlobal) 131 | return s 132 | } 133 | -------------------------------------------------------------------------------- /backends/bstar/block.go: -------------------------------------------------------------------------------- 1 | package bstar 2 | 3 | import ( 4 | "github.com/Nv7-Github/bsharp/ir" 5 | ) 6 | 7 | func (b *BStar) buildNodes(n []ir.Node) ([]Node, error) { 8 | out := make([]Node, 0) 9 | for _, n := range n { 10 | node, err := b.buildNode(n) 11 | if err != nil { 12 | return nil, err 13 | } 14 | _, ok := node.(*BlockNode) 15 | if ok && node.(*BlockNode).DoesPrint { 16 | node = b.noPrint(node) 17 | } 18 | out = append(out, node) 19 | } 20 | return out, nil 21 | } 22 | 23 | func (b *BStar) buildBlock(n *ir.BlockNode) (Node, error) { 24 | switch bl := n.Block.(type) { 25 | case *ir.IfNode: 26 | cond, err := b.buildNode(bl.Condition) 27 | if err != nil { 28 | return nil, err 29 | } 30 | bod, err := b.buildNodes(bl.Body) 31 | if err != nil { 32 | return nil, err 33 | } 34 | bod = append(bod, b.noPrintNode()) // make sure it doesnt return anything 35 | if bl.Else == nil { 36 | return blockNode(false, constNode("IF"), cond, blockNode(false, append([]Node{constNode("BLOCK")}, bod...)...), b.noPrintNode()), nil 37 | } 38 | els, err := b.buildNodes(bl.Else) 39 | if err != nil { 40 | return nil, err 41 | } 42 | els = append(els, b.noPrintNode()) // make sure it doesnt return anything 43 | return blockNode(false, constNode("IF"), cond, blockNode(false, append([]Node{constNode("BLOCK")}, bod...)...), blockNode(false, append([]Node{constNode("BLOCK")}, els...)...)), nil 44 | 45 | case *ir.WhileNode: 46 | cond, err := b.buildNode(bl.Condition) 47 | if err != nil { 48 | return nil, err 49 | } 50 | bod, err := b.buildNodes(bl.Body) 51 | if err != nil { 52 | return nil, err 53 | } 54 | bod = append([]Node{constNode("WHILE"), cond}, blockNode(false, append([]Node{constNode("BLOCK")}, append(bod, b.noPrintNode())...)...)) 55 | return blockNode(true, bod...), nil 56 | 57 | case *ir.SwitchNode: 58 | cnd, err := b.buildNode(bl.Value) 59 | if err != nil { 60 | return nil, err 61 | } 62 | end := b.noPrintNode() 63 | if bl.Default != nil { 64 | body, err := b.buildNodes(bl.Default.Block.(*ir.Default).Body) 65 | if err != nil { 66 | return nil, err 67 | } 68 | body = append(body, b.noPrintNode()) 69 | end = blockNode(false, append([]Node{constNode("BLOCK")}, body...)...) 70 | } 71 | bod, err := b.buildCase(cnd, bl.Cases, end) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return bod, nil 76 | 77 | default: 78 | return nil, n.Pos().Error("unknown block: %T", bl) 79 | } 80 | } 81 | 82 | func (b *BStar) buildCase(cnd Node, cs []*ir.BlockNode, end Node) (Node, error) { 83 | ca := cs[0].Block.(*ir.Case) 84 | cond, err := b.buildNode(ca.Value) 85 | if err != nil { 86 | return nil, err 87 | } 88 | body, err := b.buildNodes(ca.Body) 89 | if err != nil { 90 | return nil, err 91 | } 92 | body = append(body, b.noPrintNode()) 93 | if len(cs) > 1 { 94 | end, err = b.buildCase(cnd, cs[1:], end) 95 | if err != nil { 96 | return nil, err 97 | } 98 | } 99 | return blockNode(false, constNode("IF"), blockNode(true, constNode("COMPARE"), cnd, constNode("=="), cond), blockNode(false, append([]Node{constNode("BLOCK")}, body...)...), end), nil 100 | } 101 | -------------------------------------------------------------------------------- /bot/editcmd.go: -------------------------------------------------------------------------------- 1 | package bot 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | 8 | "github.com/Nv7-Github/sevcord" 9 | ) 10 | 11 | func EditCmd(b *Bot) sevcord.SlashCommandObject { 12 | return &sevcord.SlashCommandGroup{ 13 | Name: "edit", 14 | Description: "Edit a B# tag!", 15 | Children: []sevcord.SlashCommandObject{ 16 | &sevcord.SlashCommand{ 17 | Name: "tag", 18 | Description: "Edit a B# tag from the source code of a B# program!", 19 | Options: []sevcord.Option{ 20 | { 21 | Name: "id", 22 | Description: "The ID of the tag to edit!", 23 | Required: true, 24 | Autocomplete: b.Autocomplete, 25 | Kind: sevcord.OptionKindString, 26 | }, 27 | }, 28 | Handler: b.EditCodeCmd, 29 | }, 30 | &sevcord.SlashCommand{ 31 | Name: "file", 32 | Description: "Edit a B# tag from a file!", 33 | Options: []sevcord.Option{ 34 | { 35 | Name: "id", 36 | Description: "The ID of the tag to edit!", 37 | Required: true, 38 | Autocomplete: b.Autocomplete, 39 | Kind: sevcord.OptionKindString, 40 | }, 41 | { 42 | Name: "file", 43 | Description: "The file to edit the tag from!", 44 | Required: true, 45 | Kind: sevcord.OptionKindAttachment, 46 | }, 47 | }, 48 | Handler: b.EditFileCmd, 49 | }, 50 | }, 51 | } 52 | } 53 | 54 | func (b *Bot) EditCmd(ctx sevcord.Ctx, src string, id string) { 55 | dat, err := b.Get(ctx.Guild()) 56 | if Error(ctx, err) { 57 | return 58 | } 59 | ctx.Acknowledge() 60 | 61 | // Create 62 | prog, rsp := dat.GetProgram(id) 63 | if !rsp.Suc { 64 | ErrorMessage(ctx, rsp.Msg) 65 | return 66 | } 67 | 68 | // Attempt to build 69 | _, err = b.BuildCode(prog.ID+".bsp", src, ctx) 70 | if Error(ctx, err) { 71 | return 72 | } 73 | 74 | // Save 75 | err = dat.SaveSource(prog.ID, src) 76 | if Error(ctx, err) { 77 | return 78 | } 79 | ctx.Respond(sevcord.MessageResponse(fmt.Sprintf("📝 Edited tag **%s**!", prog.Name))) 80 | } 81 | 82 | func (b *Bot) EditCodeCmd(ctx sevcord.Ctx, args []any) { 83 | ctx.Modal(&sevcord.Modal{ 84 | Title: "Edit Code", 85 | Inputs: []sevcord.ModalInput{ 86 | { 87 | Label: "New Code", 88 | Style: sevcord.ModalInputStyleParagraph, 89 | Placeholder: `[PRINT "Hello, World!"]`, 90 | Required: true, 91 | MaxLength: 4000, 92 | MinLength: 1, 93 | }, 94 | }, 95 | Handler: func(ctx sevcord.Ctx, vals []string) { 96 | ctx.Acknowledge() 97 | 98 | b.EditCmd(ctx, vals[0], args[0].(string)) 99 | }, 100 | }) 101 | } 102 | 103 | func (b *Bot) EditFileCmd(ctx sevcord.Ctx, args []any) { 104 | ctx.Acknowledge() 105 | 106 | resp, err := http.Get(args[1].(*sevcord.SlashCommandAttachment).URL) 107 | if Error(ctx, err) { 108 | return 109 | } 110 | defer resp.Body.Close() 111 | dat, err := io.ReadAll(resp.Body) 112 | if Error(ctx, err) { 113 | return 114 | } 115 | if len(dat) > 1048576 { 116 | ErrorMessage(ctx, "The maximum program size is **1MB**!") 117 | return 118 | } 119 | 120 | b.EditCmd(ctx, string(dat), args[0].(string)) 121 | } 122 | -------------------------------------------------------------------------------- /bot/db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | const MaxSize = 1024 * 1024 // 1MB 11 | const MaxKeySize = 256 12 | 13 | type Resp struct { 14 | Msg string 15 | Suc bool 16 | } 17 | 18 | type Program struct { 19 | ID string 20 | Name string 21 | Creator string // User ID 22 | Uses int 23 | Created time.Time 24 | LastUsed time.Time 25 | Description string 26 | Image string 27 | } 28 | 29 | type Data struct { 30 | *sync.RWMutex 31 | names map[string]string // map[name]id 32 | Programs map[string]*Program 33 | source map[string]string 34 | Guild string 35 | path string 36 | 37 | data map[string]map[string]string 38 | datasize int 39 | dataFiles map[string]*os.File 40 | 41 | programFiles map[string]*os.File 42 | sourceFiles map[string]*os.File 43 | } 44 | 45 | func newData(path, guild string) *Data { 46 | return &Data{ 47 | RWMutex: &sync.RWMutex{}, 48 | path: path, 49 | 50 | Programs: make(map[string]*Program), 51 | Guild: guild, 52 | programFiles: make(map[string]*os.File), 53 | sourceFiles: make(map[string]*os.File), 54 | names: make(map[string]string), 55 | source: make(map[string]string), 56 | data: make(map[string]map[string]string), 57 | dataFiles: make(map[string]*os.File), 58 | } 59 | } 60 | 61 | func (d *Data) Close() { 62 | for _, f := range d.programFiles { 63 | f.Close() 64 | } 65 | for _, f := range d.sourceFiles { 66 | f.Close() 67 | } 68 | } 69 | 70 | type DB struct { 71 | *sync.RWMutex 72 | datas map[string]*Data 73 | path string 74 | } 75 | 76 | func (d *DB) Close() { 77 | for _, data := range d.datas { 78 | data.Close() 79 | } 80 | } 81 | 82 | func (d *DB) Get(gld string) (*Data, error) { 83 | d.RLock() 84 | data, exists := d.datas[gld] 85 | d.RUnlock() 86 | if !exists { 87 | // Create if not exists 88 | data = newData(filepath.Join(d.path, gld), gld) 89 | err := data.loadPrograms() 90 | if err != nil { 91 | return nil, err 92 | } 93 | err = data.loadSource() 94 | if err != nil { 95 | return nil, err 96 | } 97 | d.Lock() 98 | d.datas[gld] = data 99 | d.Unlock() 100 | } 101 | return data, nil 102 | } 103 | 104 | func newDB(path string) *DB { 105 | return &DB{ 106 | RWMutex: &sync.RWMutex{}, 107 | datas: make(map[string]*Data), 108 | path: path, 109 | } 110 | } 111 | 112 | func NewDB(path string) (*DB, error) { 113 | err := os.MkdirAll(path, os.ModePerm) 114 | if err != nil { 115 | return nil, err 116 | } 117 | 118 | db := newDB(path) 119 | folders, err := os.ReadDir(path) 120 | if err != nil { 121 | return nil, err 122 | } 123 | for _, folder := range folders { 124 | if folder.IsDir() { 125 | gld := folder.Name() 126 | data := newData(filepath.Join(path, gld), gld) 127 | err := data.loadPrograms() 128 | if err != nil { 129 | return nil, err 130 | } 131 | err = data.loadSource() 132 | if err != nil { 133 | return nil, err 134 | } 135 | err = data.loadData() 136 | if err != nil { 137 | return nil, err 138 | } 139 | db.datas[gld] = data 140 | } 141 | } 142 | return db, nil 143 | } 144 | --------------------------------------------------------------------------------