Giter VIP home page Giter VIP logo

v-lisp's Introduction

V-Lisp

A simple Lisp interpretor built in V

Features :

  • Basic functions
  • integer, floats, boolean and strings
  • cons and lists
  • define
  • quotes (including ' syntax)

Missing features :

  • Lambdas
  • Reading files

v-lisp's People

Contributors

henrixounez avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

v-lisp's Issues

lambda function

I tried to add the lambda function, code like below.

module main

import readline
import math

enum Proc {
  plus min mul div mod lt cons car cdr list eq atom sin
}

enum TokenType {
  nothing boolean integer float str token_list cons function
}

struct Token {
mut:
  typ         TokenType
  boolean     bool
  integer     int
  float       f64
  stri        string
  function    Proc
  token_list  []Token
}

struct Env {
mut:
  define_nbr  []string
  define      []Token
}

pub fn (t Token) str() string {
  match t.typ {
    .boolean    { if t.boolean { return '#t' } else { return '#f' } }
    .integer    { return '$t.integer' }
    .float      { return '$t.float' }
    .str        { return '$t.stri' }
    .token_list { return '$t.token_list' }
    .cons       { return '(${show_cons(t)})' }
    else        { return '' }
  }
}

fn show_cons(t Token) string {
  if t.token_list.len == 1 {
    return '${t.token_list[0]}'
  }
  if t.token_list[1].typ == .token_list && t.token_list[1].token_list.len == 0 {
    return '${t.token_list[0]}'
  } else if t.token_list[1].typ == .cons {
    return '${t.token_list[0]} ${show_cons(t.token_list[1])}'
  } else {
    return '${t.token_list[0]} . ${t.token_list[1].str()}' 
  }
}

fn plus(expr Token) Token {
  mut res   := 0.0
  mut float := false

  for tok in expr.token_list {
    if tok.typ == .integer {
      res += tok.integer
    } else if tok.typ == .float {
      res += tok.float
      float = true
    }
  }
  if !float {
    return Token{typ: .integer, integer: int(res)}
  } else {
    return Token{typ: .float, float: res}
  }
}

fn min(expr Token) Token {
  mut res   := 0.0
  mut float := false

  if expr.token_list.len == 1 {
    if expr.token_list[0].typ == .integer {
      res = -(expr.token_list[0].integer)
    } else if expr.token_list[0].typ == .float {
      res = -(expr.token_list[0].float)
      float = true
    }
  } else {
    if expr.token_list[0].typ == .integer {
      res = expr.token_list[0].integer
    } else if expr.token_list[0].typ == .float {
      res = expr.token_list[0].float
      float = true
    }
    expr_list := expr.token_list[1..]
    for tok in expr_list {
      if tok.typ == .integer {
        res -= tok.integer
      } else if tok.typ == .float {
        res -= tok.float
        float = true
      }
    }
  }
  if !float {
    return Token{typ: .integer, integer: int(res)}
  } else {
    return Token{typ: .float, float: res}
  }
}

fn mul(expr Token) Token {
  mut res   := 1.0
  mut float := false
  for tok in expr.token_list {
    if tok.typ == .integer {
      res *= tok.integer
    } else if tok.typ == .float {
      res *= tok.float
      float = true
    }
  }
  if !float {
    return Token{typ: .integer, integer: int(res)}
  } else {
    return Token{typ: .float, float: res}
  }
}

fn divi(expr Token) Token {
  mut res := 0.0

  if expr.token_list.len == 1 {
    if expr.token_list[0].typ == .integer {
      res = f32(1) / expr.token_list[0].integer
    } else if expr.token_list[0].typ == .float {
      res = f32(1) / expr.token_list[0].float
    }
  } else {
    if expr.token_list[0].typ == .integer {
      res = expr.token_list[0].integer
    } else if expr.token_list[0].typ == .float {
      res = expr.token_list[0].float
    }
    expr_list := expr.token_list[1..]
    for tok in expr_list {
      if tok.typ == .integer {
        res /= tok.integer
      } else if tok.typ == .float {
        res /= tok.float
      }
    }
  }
  return Token{typ: .float, float: res}
}

fn mod(expr Token) Token {
  a := if expr.token_list[0].typ == .integer {
    expr.token_list[0].integer
  } else {
    int(expr.token_list[0].float) //Cant mod floats in v :(
  }
  b := if expr.token_list[1].typ == .integer {
    expr.token_list[1].integer
  } else {
    int(expr.token_list[1].float) //Cant mod floats in v :(
  }
  return Token{typ: .integer, integer: a % b}
}

fn sin(expr Token) Token { 
  mut res := 0.0

  a := if expr.token_list[0].typ == .integer {
    expr.token_list[0].integer
  } else {
    int(expr.token_list[0].float)
  }
  res = math.sin(a)
  return Token{typ: .float, float: res}
}

fn lt(expr Token) Token {
  typ := expr.token_list[0].typ
  mut res := false
  if typ != expr.token_list[1].typ {
    panic('Not the same typ on lt')
  }
  if typ == .boolean {
    res = !expr.token_list[0].boolean && expr.token_list[1].boolean
  }
  if typ == .integer {
    res = expr.token_list[0].integer < expr.token_list[1].integer
  }
  if typ == .float {
    res = expr.token_list[0].float < expr.token_list[1].float
  }
  if typ == .str {
    res = expr.token_list[0].stri < expr.token_list[1].stri
  }
  return Token{typ: .boolean, boolean: res}
}

fn cons(expr Token) Token {
  return Token{typ: .cons, token_list: [expr.token_list[0], expr.token_list[1]]}
}

fn car(expr Token) Token {
  return expr.token_list[0].token_list[0]
}

fn cdr(expr Token) Token {
  to_take := expr.token_list[0]
  if to_take.typ == .cons {
    return to_take.token_list[1]
  } else {
    return Token{typ: to_take.typ, token_list: to_take.token_list[1..]}
  }
}

fn list(expr Token) Token {
  return Token{typ: .token_list, token_list: expr.token_list}
}

fn eq(expr Token) Token {
  typ := expr.token_list[0].typ
  mut res := false
  if typ != expr.token_list[1].typ {
    println('Not the same typ on eq')
    return Token{typ: .nothing}
    //panic('Not the same typ on eq')
  }
  else if typ == .boolean {
    res = expr.token_list[0].boolean == expr.token_list[1].boolean
  }
  else if typ == .integer {
    res = expr.token_list[0].integer == expr.token_list[1].integer
  }
  else if typ == .float {
    res = expr.token_list[0].float == expr.token_list[1].float
  }
  else if typ == .str {
    res = expr.token_list[0].stri == expr.token_list[1].stri
  }
  else if typ == .token_list {
    res = expr.token_list[0].token_list == expr.token_list[1].token_list
  }
  return Token{typ: .boolean, boolean: res}
}

fn atom(expr Token) Token {
  res := expr.token_list[0].typ != .token_list && expr.token_list[0].typ != .cons
  return Token{typ: .boolean, boolean: res}
}

fn call_func(call Token, expr Token) Token {
  match call.function {
    .plus { return plus(expr) }
    .min  { return min(expr)  }
    .mul  { return mul(expr)  }
    .div  { return divi(expr) }
    .mod  { return mod(expr)  }
    .lt   { return lt(expr)   }
    .cons { return cons(expr) }
    .car  { return car(expr)  }
    .cdr  { return cdr(expr)  }
    .list { return list(expr) }
    .eq   { return eq(expr)   }
    .atom { return atom(expr) }
    .sin  { return sin(expr)  }
    // else { Token{typ: .nothing }}
  }
  return Token{typ: .str, stri: 'nothing'}
}

fn eval(expr Token, mut env Env) ?Token {
  if expr.typ == .str {    
    for nbr := env.define_nbr.len-1; nbr >= 0; nbr-- {
      if expr.stri == env.define_nbr[nbr] {
        return env.define[nbr]
      }
    }
    if expr.stri == '*env*' {
      println('env: ${env}')
    } else {  
      println('unknown $expr.stri')
    }
    return Token{typ: .nothing}
 
  } else if expr.typ == .integer || expr.typ == .float || expr.typ == .boolean {
      return expr
  } else if expr.token_list.len >= 2 && expr.token_list[0].stri == 'quote' {
      return expr.token_list[1]
  } else if expr.token_list.len == 3 && expr.token_list[0].stri == 'define' {
      v_vars := expr.token_list[1]
      v_expr := expr.token_list[2]
      env.define_nbr << v_vars.stri 
      if v_expr.typ == .token_list && v_expr.token_list[0].stri == 'lambda' {
        env.define << v_expr
      } else {
        env.define << eval(v_expr, mut env) or { return err }
      }

  } else if expr.token_list.len >= 2 && expr.token_list[0].stri == 'cond' {
      for i := 1; i < expr.token_list.len; i++ {
        if expr.token_list[i].token_list.len < 2 {
          println('cond list too small')
          return Token{typ: .nothing}

        }
        res := eval(expr.token_list[i].token_list[0], mut env) or { return err }
        if res.boolean {
          return eval(expr.token_list[i].token_list[1], mut env)
        }
      }
  } else if expr.token_list.len == 2 && 
            expr.token_list[0].typ == .token_list &&
            expr.token_list[0].token_list[0].stri == 'lambda' {
        v_vars := expr.token_list[0].token_list[1]
        v_expr := expr.token_list[0].token_list[2]
        v_args := eval(expr.token_list[1], mut env) or { return err }
        mut v_env  := env
        mut new_tok := Token{typ: .str, stri: 'nothing'}
        for i := 0; i < v_vars.token_list.len; i++ {
          match v_args.typ { 
            .token_list { new_tok = v_args.token_list[i] }
            else { new_tok = v_args }
          }
          
          v_env.define_nbr << v_vars.token_list[i].stri
          v_env.define << new_tok  
        }      
        return eval(v_expr, mut v_env) 
  } else {
    if expr.token_list.len == 0 {
      return Token{typ: .token_list, token_list: []}
    }

    call := eval(expr.token_list[0], mut env) or { return err }
    if call.typ == .token_list && call.token_list[0].stri == 'lambda' {
      mut new_tok := Token{typ: .token_list, token_list: []}      
      for i := 1; i < expr.token_list.len; i++ {
        v_tok := eval(expr.token_list[i],mut env) or { return err }
        new_tok.token_list << v_tok
      }
      mut v_args := Token{typ: .token_list, token_list: [Token{typ: .str, stri: 'quote'}, new_tok]} //須加quote開頭 
      v_expr := Token{typ: .token_list, token_list: [call, v_args]}
      return eval(v_expr, mut env) 
    }
    typ := if call.function == .cons { TokenType.cons } else { TokenType.token_list }
    mut new_expr := Token{typ: typ, token_list: []}
    for i := 1; i < expr.token_list.len; i++ {
      new_tok := eval(expr.token_list[i],mut env) or { return err }
      new_expr.token_list << new_tok
    }
    return call_func(call, new_expr)
  }
  return Token{typ: .nothing}
}

fn parse(mut expr []string) ?Token {
  if expr.len <= 1 {
    println('unexpected eof')
    return Token{typ: .nothing}

  }
  mut tok := expr[0] 
  expr.delete(0)

  match tok {
    '#t' { return Token{typ: .boolean, boolean: true} }
    '#f' { return Token{typ: .boolean, boolean: false} }
    'null' { return Token{typ: .token_list, token_list: []} }
    '\'' {
          mut new_list := Token{typ: .token_list, token_list: [Token{typ: .str, stri: 'quote'}]}
          new_expr := parse(mut expr) or { return err }
          new_list.token_list << new_expr
          return new_list
         }
    '('  {
          mut new_list := Token{typ: .token_list, token_list: []} // new_list = []
          for expr[0] != ')' {
            new_expr := parse(mut expr) or { return err }
            new_list.token_list << new_expr // new_list = [new_expr]
            if expr.len == 1 {
              println('unexpected eof')
              return Token{typ: .nothing}

            }
          }
          expr.delete(0)
          return new_list
         }
    ')'  { 
          println('unexpected )')
          return Token{typ: .nothing}

         }
    else {}
  }

  if tok[0].is_digit() {
    if tok.contains('.') {
      return Token{typ: .float, float: tok.f32()}
    } else {
      return Token{typ: .integer, integer: tok.int()}
    }
  }

  return Token{typ: .str, stri: tok}
}

fn token(line string) [] string {
  mut list := []string{}
  mut str := ''  

  for c in line {  
    mut ch := c.ascii_str()
    match ch {
      '(',')','\'',' ' { 
              if str.len>0 {        
                list << str
                str = ''        
              }
              if ch != ' ' { list << ch }
           }
      else { str += ch }
    }
  }
  if str != '' && str != '\n'{
    list << str.replace('\n','') 
  }
  return list
}  

fn init_env() Env {
  return Env{
    define_nbr: [
      '+',     
      '-',     
      '*',     
      '/',     
      '%',     
      '<',     
      'cons',
      'car',  
      'cdr',   
      'list',  
      'eq?',   
      'atom?', 
      'sin'   
    ]
    define: [
      Token{ typ: .function, function: .plus},
      Token{ typ: .function, function: .min},
      Token{ typ: .function, function: .mul},
      Token{ typ: .function, function: .div},
      Token{ typ: .function, function: .mod},
      Token{ typ: .function, function: .lt},
      Token{ typ: .function, function: .cons},
      Token{ typ: .function, function: .car},
      Token{ typ: .function, function: .cdr},
      Token{ typ: .function, function: .list},
      Token{ typ: .function, function: .eq},
      Token{ typ: .function, function: .atom},
      Token{ typ: .function, function: .sin}
    ]
  }
}

fn repl(){
  mut env  := init_env()  
  mut rl   := readline.Readline{}  
  mut line := ''
  for {
    line = ''
    print('vlisp> ')
    line = rl.read_line('') or { exit(1) }

    mut expr := token(line)
    expr << ' '

    for expr[0] != ' ' {
      expr_list := parse(mut expr)       or { println(err) return }
      result := eval(expr_list, mut env) or { println(err) return }
      if result.str() != '' {
        println(result)
      }
    }

    if !(line.len > 0 && line != '') {
      break
    }
  }
}

fn main() {
  repl()
}

Update V

This repo code needs to be updated with latest revision of V else this does not compile.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.