Created
November 4, 2019 03:38
-
-
Save eatonphil/8825e930af50e4daf42ea61808e474d6 to your computer and use it in GitHub Desktop.
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
package main | |
import ( | |
"fmt" | |
"go/ast" | |
"go/parser" | |
"go/token" | |
"io/ioutil" | |
"log" | |
"os" | |
"reflect" | |
"strconv" | |
) | |
type kind uint | |
const ( | |
i64 kind = iota | |
fn | |
bl | |
) | |
type value struct { | |
kind kind | |
value interface{} | |
} | |
type context map[string]value | |
func (c context) copy() context { | |
cpy := context{} | |
for key, value := range c { | |
cpy[key] = value | |
} | |
return cpy | |
} | |
type ret struct { | |
set bool | |
values []value | |
} | |
func (r *ret) setValue(v value) { | |
r.values = []value{v} | |
r.set = true | |
} | |
func (r *ret) setValues(values []value) { | |
r.values = values | |
r.set = true | |
} | |
func interpretCallExpr(ctx context, r *ret, ce *ast.CallExpr) { | |
var fnr ret | |
interpretExpr(ctx, &fnr, ce.Fun) | |
fn := fnr.values[0] | |
values := []value{} | |
for _, arg := range ce.Args { | |
var vr ret | |
interpretExpr(ctx, &vr, arg) | |
values = append(values, vr.values[0]) | |
} | |
fn.value.(func(context, *ret, []value))(ctx, r, values) | |
} | |
func interpretBinaryExpr(ctx context, r *ret, bexpr *ast.BinaryExpr) { | |
var xr, yr ret | |
interpretExpr(ctx, &xr, bexpr.X) | |
x := xr.values[0] | |
interpretExpr(ctx, &yr, bexpr.Y) | |
y := yr.values[0] | |
switch bexpr.Op { | |
case token.ADD: | |
r.setValue(value{i64, x.value.(int64) + y.value.(int64)}) | |
case token.SUB: | |
r.setValue(value{i64, x.value.(int64) - y.value.(int64)}) | |
case token.EQL: | |
r.setValue(value{bl, x.value.(int64) == y.value.(int64)}) | |
default: | |
log.Fatalf("Unknown binary expression type: %+v", bexpr) | |
} | |
} | |
func interpretExpr(ctx context, r *ret, expr ast.Expr) { | |
switch e := expr.(type) { | |
case *ast.BinaryExpr: | |
interpretBinaryExpr(ctx, r, e) | |
case *ast.CallExpr: | |
interpretCallExpr(ctx, r, e) | |
case *ast.Ident: | |
r.setValue(ctx[e.Name]) | |
case *ast.BasicLit: | |
switch e.Kind { | |
case token.INT: | |
i, _ := strconv.ParseInt(e.Value, 10, 64) | |
r.setValue(value{i64, i}) | |
default: | |
log.Fatalf("Unknown basiclit type: %+v", e) | |
} | |
default: | |
log.Fatalf("Unknown expr type (%s): %+v", reflect.TypeOf(e), e) | |
} | |
} | |
func interpretIfStmt(ctx context, r *ret, is *ast.IfStmt) { | |
interpretStmt(ctx, nil, is.Init) | |
var cr ret | |
interpretExpr(ctx, &cr, is.Cond) | |
c := cr.values[0] | |
if c.value.(bool) { | |
interpretBlockStmt(ctx, r, is.Body) | |
return | |
} | |
interpretStmt(ctx, r, is.Else) | |
} | |
func interpretReturnStmt(ctx context, r *ret, s *ast.ReturnStmt) { | |
var values []value | |
for _, expr := range s.Results { | |
var r ret | |
interpretExpr(ctx, &r, expr) | |
values = append(values, r.values[0]) | |
} | |
r.setValues(values) | |
return | |
} | |
func interpretStmt(ctx context, r *ret, stmt ast.Stmt) { | |
if stmt == nil { | |
return | |
} | |
switch s := stmt.(type) { | |
case *ast.ReturnStmt: | |
interpretReturnStmt(ctx, r, s) | |
case *ast.IfStmt: | |
interpretIfStmt(ctx, r, s) | |
case *ast.ExprStmt: | |
interpretExpr(ctx, r, s.X) | |
default: | |
log.Fatalf("Unknown stmt type (%s): %+v", reflect.TypeOf(s), s) | |
} | |
} | |
func interpretBlockStmt(ctx context, r *ret, bs *ast.BlockStmt) { | |
for _, stmt := range bs.List { | |
interpretStmt(ctx, r, stmt) | |
if r.set { | |
return | |
} | |
} | |
} | |
func interpretFuncDecl(ctx context, r *ret, fd *ast.FuncDecl) { | |
ctx[fd.Name.String()] = value{ | |
fn, | |
func(ctx context, r *ret, args []value) { | |
childCtx := ctx.copy() | |
for i, param := range fd.Type.Params.List { | |
childCtx[param.Names[0].String()] = args[i] | |
} | |
interpretBlockStmt(childCtx, r, fd.Body) | |
}, | |
} | |
} | |
func interpret(ctx context, f *ast.File) { | |
for _, decl := range f.Decls { | |
switch d := decl.(type) { | |
case *ast.FuncDecl: | |
interpretFuncDecl(ctx, nil, d) | |
default: | |
log.Fatalf("Unknown decl type (%s): %+v", reflect.TypeOf(d), d) | |
} | |
} | |
} | |
func main() { | |
fset := token.NewFileSet() // positions are relative to fset | |
src, err := ioutil.ReadFile(os.Args[1]) | |
if err != nil { | |
log.Fatalf("Unable to read file: %s", err.Error()) | |
} | |
f, err := parser.ParseFile(fset, os.Args[1], src, 0) | |
if err != nil { | |
log.Fatalf("Unable to parse file: %s", err.Error()) | |
} | |
ctx := context{} | |
ctx["println"] = value{ | |
fn, | |
func(ctx context, r *ret, args []value) { | |
var values []interface{} | |
for _, arg := range args { | |
values = append(values, arg.value) | |
} | |
fmt.Println(values...) | |
}, | |
} | |
interpret(ctx, f) | |
var r ret | |
ctx["main"].value.(func(context, *ret, []value))(ctx, &r, []value{}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment