Skip to content

Instantly share code, notes, and snippets.

@eatonphil
Created November 4, 2019 03:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eatonphil/8825e930af50e4daf42ea61808e474d6 to your computer and use it in GitHub Desktop.
Save eatonphil/8825e930af50e4daf42ea61808e474d6 to your computer and use it in GitHub Desktop.
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