-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
16,801 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,328 @@ | ||
package ast | ||
|
||
import ( | ||
"github.com/skius/stringlang/token" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
func NewContext(args []string, funcs map[string]func([]string)string) Context { | ||
return Context { | ||
Args: args, | ||
VariableMap: make(map[Var]Val), | ||
FunctionMap: funcs, | ||
} | ||
} | ||
|
||
type Context struct { | ||
Args []string | ||
VariableMap map[Var]Val | ||
FunctionMap map[string]func([]string)string | ||
MaxStackSize int64 | ||
MaxWhileIter int | ||
} | ||
|
||
type Attrib interface {} | ||
|
||
type Expr interface { | ||
Eval(Context) Val | ||
String() string | ||
} | ||
|
||
type Val string | ||
const False = Val("") | ||
func NewVal(a Attrib) (Expr, error) { | ||
quoted := attribToString(a) | ||
return Val(quoted[1:len(quoted)-1]), nil | ||
} | ||
func (v Val) Eval(c Context) Val { | ||
return v | ||
} | ||
func (v Val) String() string { | ||
return "\"" + string(v) + "\"" | ||
} | ||
|
||
type ExprSeq []Expr | ||
func NewExprSeq() (Expr, error) { | ||
return ExprSeq([]Expr{}), nil | ||
} | ||
func ExprSeqAppend(s, e Attrib) (Expr, error) { | ||
seq := s.(ExprSeq) | ||
exp := e.(Expr) | ||
seq2 := append(seq, exp) | ||
return seq2, nil | ||
} | ||
func (es ExprSeq) Eval(c Context) Val { | ||
var last Val | ||
for _, exp := range es { | ||
last = exp.Eval(c) | ||
} | ||
return last | ||
} | ||
func (es ExprSeq) String() string { | ||
str := "" | ||
for i, exp := range es { | ||
if i < len(es) - 1 { | ||
str += exp.String() + ";\n" | ||
} else { | ||
str += exp.String() | ||
} | ||
} | ||
return str | ||
} | ||
|
||
type Arg int | ||
func NewArg(i Attrib) (Expr, error) { | ||
s := attribToString(i) | ||
intValue, err := strconv.Atoi(s) | ||
return Arg(intValue), err | ||
} | ||
func (a Arg) Eval(c Context) Val { | ||
if int(a) > len(c.Args) { | ||
return "" | ||
} | ||
return Val(c.Args[a]) | ||
} | ||
func (a Arg) String() string { | ||
return "$" + strconv.Itoa(int(a)) | ||
} | ||
|
||
type Equals struct { | ||
A Expr | ||
B Expr | ||
} | ||
func NewEquals(a, b Attrib) (Expr, error) { | ||
return Equals{A: a.(Expr), B: b.(Expr)}, nil | ||
} | ||
func (e Equals) Eval(c Context) Val { | ||
val := "false" | ||
if e.A.Eval(c) == e.B.Eval(c) { | ||
val = "true" | ||
} | ||
return Val(val) | ||
} | ||
func (e Equals) String() string { | ||
return e.A.String() + " == " + e.B.String() | ||
} | ||
|
||
type NotEquals struct { | ||
A Expr | ||
B Expr | ||
} | ||
func NewNotEquals(a, b Attrib) (Expr, error) { | ||
return NotEquals{A: a.(Expr), B: b.(Expr)}, nil | ||
} | ||
func (e NotEquals) Eval(c Context) Val { | ||
val := "false" | ||
if e.A.Eval(c) != e.B.Eval(c) { | ||
val = "true" | ||
} | ||
return Val(val) | ||
} | ||
func (e NotEquals) String() string { | ||
return e.A.String() + " != " + e.B.String() | ||
} | ||
|
||
type Or struct { | ||
A Expr | ||
B Expr | ||
} | ||
func NewOr(a, b Attrib) (Expr, error) { | ||
return Or{A: a.(Expr), B: b.(Expr)}, nil | ||
} | ||
func (o Or) Eval(c Context) Val { | ||
if boolOf(o.A.Eval(c)) || boolOf(o.B.Eval(c)) { | ||
return Val("true") | ||
} else { | ||
return Val("false") | ||
} | ||
} | ||
func (o Or) String() string { | ||
return o.A.String() + " || " + o.B.String() | ||
} | ||
|
||
type And struct { | ||
A Expr | ||
B Expr | ||
} | ||
func NewAnd(a, b Attrib) (Expr, error) { | ||
return And{A: a.(Expr), B: b.(Expr)}, nil | ||
} | ||
func (a And) Eval(c Context) Val { | ||
if boolOf(a.A.Eval(c)) && boolOf(a.B.Eval(c)) { | ||
return Val("true") | ||
} else { | ||
return Val("false") | ||
} | ||
} | ||
func (a And) String() string { | ||
return a.A.String() + " && " + a.B.String() | ||
} | ||
|
||
type Concat struct { | ||
A Expr | ||
B Expr | ||
} | ||
func NewConcat(a, b Attrib) (Expr, error) { | ||
return Concat{A: a.(Expr), B: b.(Expr)}, nil | ||
} | ||
func (cc Concat) Eval(c Context) Val { | ||
return cc.A.Eval(c) + cc.B.Eval(c) | ||
} | ||
func (cc Concat) String() string { | ||
return cc.A.String() + " + " + cc.B.String() | ||
} | ||
|
||
type Var string | ||
func NewVar(a Attrib) (Expr, error) { | ||
return Var(attribToString(a)), nil | ||
} | ||
func (v Var) Eval(c Context) Val { | ||
return c.VariableMap[v] | ||
} | ||
func (v Var) String() string { | ||
return string(v) | ||
} | ||
|
||
type Call struct { | ||
Fn Var | ||
Args []Expr | ||
} | ||
func NewCall(f, as Attrib) (Expr, error) { | ||
fn := f.(Var) | ||
args := as.(ExprSeq) | ||
return Call {Fn: fn, Args: args}, nil | ||
} | ||
func (ca Call) Eval(c Context) Val { | ||
vals := make([]string, 0, len(ca.Args)) | ||
for _, argExp := range ca.Args { | ||
v := argExp.Eval(c) | ||
vals = append(vals, string(v)) | ||
} | ||
fn, ok := c.FunctionMap[string(ca.Fn)] | ||
if !ok { | ||
panic("function '" + string(ca.Fn) + "' not found.") | ||
} | ||
res := fn(vals) | ||
return Val(res) | ||
} | ||
func (ca Call) String() string { | ||
args := make([]string, 0, len(ca.Args)) | ||
for _, arg := range ca.Args { | ||
args = append(args, arg.String()) | ||
} | ||
|
||
return ca.Fn.String() + "(" + strings.Join(args, ", ") + ")" | ||
} | ||
|
||
type Index struct { | ||
Source Expr | ||
I Expr | ||
} | ||
func NewIndex(s, i Attrib) (Expr, error) { | ||
return Index {Source: s.(Expr), I: i.(Expr)}, nil | ||
} | ||
func NewIndexInt(s, i Attrib) (Expr, error) { | ||
return Index {Source: s.(Expr), I: Val(attribToString(i))}, nil | ||
} | ||
func (i Index) Eval(c Context) Val { | ||
src := string(i.Source.Eval(c)) | ||
idx, err := strconv.Atoi(string(i.I.Eval(c))) | ||
if err != nil { | ||
return Val("") | ||
} | ||
if idx >= len(src) { | ||
return Val("") | ||
} | ||
return Val(src[idx]) | ||
} | ||
func (i Index) String() string { | ||
return i.Source.String() + "[" + i.I.String() + "]" | ||
} | ||
|
||
type Assn struct { | ||
V Var | ||
E Expr | ||
} | ||
func NewAssn(v, e Attrib) (Expr, error) { | ||
va := v.(Var) | ||
ex := e.(Expr) | ||
return Assn {V: va, E: ex}, nil | ||
} | ||
func (a Assn) Eval(c Context) Val { | ||
newVal := a.E.Eval(c) | ||
c.VariableMap[a.V] = newVal | ||
return newVal | ||
} | ||
func (a Assn) String() string { | ||
return a.V.String() + " = " + a.E.String() | ||
} | ||
|
||
type IfElse struct { | ||
Cond Expr | ||
Then Expr | ||
Else Expr | ||
} | ||
func NewIfElse(c, t, e Attrib) (Expr, error) { | ||
co := c.(Expr) | ||
th := t.(Expr) | ||
el := e.(Expr) | ||
return IfElse{Cond: co, Then: th, Else: el}, nil | ||
} | ||
func (e IfElse) Eval(c Context) Val { | ||
if boolOf(e.Cond.Eval(c)) { | ||
return e.Then.Eval(c) | ||
} else { | ||
return e.Else.Eval(c) | ||
} | ||
} | ||
func (e IfElse) String() string { | ||
str := "if (" + e.Cond.String() + ") {\n\t" + e.Then.String() + "\n} else {\n\t" + e.Else.String() + "\n}" | ||
return str | ||
} | ||
|
||
type While struct { | ||
Cond Expr | ||
Body Expr | ||
} | ||
func NewWhile(c, b Attrib) (Expr, error) { | ||
co := c.(Expr) | ||
bo := b.(Expr) | ||
return While{Cond: co, Body: bo}, nil | ||
} | ||
func (e While) Eval(c Context) Val { | ||
var cond Val = e.Cond.Eval(c) | ||
var body Val | ||
steps := 0 | ||
for boolOf(cond) { | ||
body = e.Body.Eval(c) | ||
cond = e.Cond.Eval(c) | ||
|
||
if c.MaxWhileIter > -1 && steps > c.MaxWhileIter { | ||
break | ||
} | ||
if c.MaxStackSize > -1 && checkSize(c.VariableMap) > c.MaxStackSize { | ||
break | ||
} | ||
steps++ | ||
} | ||
return body | ||
} | ||
func (e While) String() string { | ||
str := "while (" + e.Cond.String() + ") {\n\t" + e.Body.String() + "\n}" | ||
return str | ||
} | ||
|
||
|
||
func attribToString(a Attrib) string { | ||
return string(a.(*token.Token).Lit) | ||
} | ||
func boolOf(v Val) bool { | ||
return v != "false" && v != "" | ||
} | ||
func checkSize(m map[Var]Val) (total int64) { | ||
for k, v := range m { | ||
total += int64(len(k)) + int64(len(v)) | ||
} | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"github.com/skius/stringlang" | ||
"io/ioutil" | ||
"math/rand" | ||
"os" | ||
"strconv" | ||
"strings" | ||
"time" | ||
) | ||
|
||
func main() { | ||
if len(os.Args) < 2 { | ||
fmt.Println("Usage: ./stringlang <program.stringlang> ..<args>") | ||
return | ||
} | ||
file := os.Args[1] | ||
content, err := ioutil.ReadFile(file) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
expr, err := stringlang.Parse(content) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
rand.Seed(time.Now().UnixNano()) | ||
funcs := map[string]func([]string)string { | ||
"random": func(args []string) string { | ||
num := len(args) | ||
if num == 0 { | ||
return strconv.Itoa(rand.Intn(10) + 1) | ||
} else if num == 1 { | ||
val, err := strconv.Atoi(args[0]) | ||
if err == nil { | ||
return strconv.Itoa(rand.Intn(val) + 1) | ||
} | ||
} | ||
return args[rand.Intn(num)] | ||
}, | ||
"length" : func(args []string) string { | ||
return strconv.Itoa(len(args[0])) | ||
}, | ||
} | ||
|
||
// We want the .stringlang file, and not the interpreter's path to be argument 0 | ||
args := make([]string, len(os.Args) - 1) | ||
for i := 1; i < len(os.Args); i++ { | ||
args[i-1] = os.Args[i] | ||
} | ||
|
||
ctx := stringlang.NewContext(args, funcs) | ||
ctx.MaxWhileIter = 10000 | ||
ctx.MaxStackSize = 100 * 1024 * 1024 // 100MB limit for variables | ||
|
||
result := string(expr.Eval(ctx)) | ||
|
||
fmt.Println("Returns:") | ||
fmt.Println(strings.ReplaceAll(result, `\n`, "\n")) | ||
} |
Oops, something went wrong.