diff --git a/.gitignore b/.gitignore index 9b9aaf0..719ef32 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ print_ast *~ *.swp *.swo + +# notes +*.txt* diff --git a/ast/print.go b/ast/print.go index 8727b74..2460603 100644 --- a/ast/print.go +++ b/ast/print.go @@ -30,7 +30,7 @@ func (t *Token) String() string { case STRING: return "\"" + t.Inner.(string) + "\"" - case NUMBER: + case NUMBER, BOOL: return t.Inner.(string) case LIST: diff --git a/ast/token.go b/ast/token.go index 9d7bbef..69978cd 100644 --- a/ast/token.go +++ b/ast/token.go @@ -28,6 +28,7 @@ const ( STRING Token_t = iota NUMBER Token_t = iota SYMBOL Token_t = iota + BOOL Token_t = iota ) type Token struct { diff --git a/stdlib/bool.go b/stdlib/bool.go new file mode 100644 index 0000000..df2c413 --- /dev/null +++ b/stdlib/bool.go @@ -0,0 +1,155 @@ +/* SHS: Syntactically Homogeneous Shell + * Copyright (C) 2019 Aidan Hahn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package stdlib + +import ( + "strconv" + "gitlab.com/whom/shs/log" + "gitlab.com/whom/shs/ast" +) + +// lt, gt + +func not(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + if in.Tag != ast.BOOL { + log.Log(log.ERR, "non-bool argument to 'not'", "not") + return nil + } + + out := "T" + if in.Inner.(string) == "T" { + out = "F" + } + + return &ast.Token{Tag: ast.BOOL, Inner: out} +} + +func eq(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + out := "T" + second := in.Next + + if in.Tag != second.Tag { + out = "F" + } else { + switch in.Tag { + case ast.LIST: + // returns true if difference found + var consume_list func(*ast.Token, *ast.Token) bool + consume_list = func(l *ast.Token, r *ast.Token) bool { + if (l == nil && r != nil) || (r == nil && l != nil) { + return true + } + + if l.Tag != r.Tag { + return true + } + + l_iter := l + r_iter := r + for l_iter != nil { + if r_iter == nil || l_iter.Tag != r_iter.Tag { + return true + } + + if l_iter.Tag == ast.LIST { + diff := consume_list(l_iter.Inner.(*ast.Token), + r_iter.Inner.(*ast.Token)) + if diff { + return true + } + + } else { + if l_iter.Inner.(string) != r_iter.Inner.(string) { + return true + } + } + + l_iter = l_iter.Next + r_iter = r_iter.Next + } + + if r_iter != nil { + return true + } else { + return false + } + } + + if consume_list(in.Inner.(*ast.Token), second.Inner.(*ast.Token)) { + out = "F" + } + + case ast.NUMBER, ast.STRING, ast.BOOL: + if in.Inner.(string) != second.Inner.(string) { + out = "F" + } + } + } + + return &ast.Token{Tag: ast.BOOL, Inner: out} +} + +func lt(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + out := "T" + + second := in.Next + if in.Tag != ast.NUMBER || second.Tag != ast.NUMBER { + log.Log(log.ERR, "non-number argument to numeric boolean operator", ">/<=") + return nil + } + + l, _ := strconv.ParseInt(in.Inner.(string), 10, 64) + r, _ := strconv.ParseInt(second.Inner.(string), 10, 64) + + if l >= r { + out = "F" + } + + return &ast.Token{Tag: ast.BOOL, Inner: out} +} + +func gt(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + out := "T" + + second := in.Next + if in.Tag != ast.NUMBER || second.Tag != ast.NUMBER { + log.Log(log.ERR, "non-number argument to numeric boolean operator", ">/<=") + return nil + } + + l, _ := strconv.ParseInt(in.Inner.(string), 10, 64) + r, _ := strconv.ParseInt(second.Inner.(string), 10, 64) + + if l <= r { + out = "F" + } + + return &ast.Token{Tag: ast.BOOL, Inner: out} +} + +func ne(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + return not(eq(in, vt, ft), vt, ft) +} + +func gte(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + return not(lt(in, vt, ft), vt, ft) +} + +func lte(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + return not(gt(in, vt, ft), vt, ft) +} diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index e4efcde..806c0f0 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -140,6 +140,55 @@ func GenFuncTable() ast.FuncTable { }, */ + "eq": &ast.Function{ + Function: eq, + Name: "==", + TimesCalled: 0, + Args: 2, + }, + + "ne": &ast.Function{ + Function: ne, + Name: "!=", + TimesCalled: 0, + Args: 2, + }, + + "<": &ast.Function{ + Function: lt, + Name: "<", + TimesCalled: 0, + Args: 2, + }, + + ">": &ast.Function{ + Function: gt, + Name: ">", + TimesCalled: 0, + Args: 2, + }, + + "<=": &ast.Function{ + Function: lte, + Name: "<=", + TimesCalled: 0, + Args: 2, + }, + + ">=": &ast.Function{ + Function: gte, + Name: ">=", + TimesCalled: 0, + Args: 2, + }, + + "!": &ast.Function{ + Function: not, + Name: "!", + TimesCalled: 0, + Args: 1, + }, + "jobs": &ast.Function{ Function: jobs, Name: "list jobs",