- Basic functions
- integer, floats, boolean and strings
- cons and lists
- define
- quotes (including
'
syntax)
- Lambdas
- Reading files
Lisp interpretor in V
This repo code needs to be updated with latest revision of V else this does not compile.
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() }
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.