Open forthlee opened 2 years ago
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() }
You can do a Pull Request if you want to :) Its hard to see the differences when looking only at the whole project ^^
I tried to add the lambda function, code like below.