mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: initial interpreter code (#12605)
This commit is contained in:
@@ -4,96 +4,198 @@
|
||||
module eval
|
||||
|
||||
import v.ast
|
||||
import v.checker
|
||||
import v.pref
|
||||
import v.util
|
||||
|
||||
pub type Object = int | string
|
||||
pub fn new_eval(table &ast.Table, pref &pref.Preferences) Eval {
|
||||
return Eval{
|
||||
table: table
|
||||
pref: pref
|
||||
}
|
||||
}
|
||||
|
||||
// const/global is `Object`
|
||||
type Symbol = Object | ast.EmptyStmt | ast.FnDecl
|
||||
|
||||
pub struct Eval {
|
||||
mut:
|
||||
checker checker.Checker
|
||||
vars map[string]Var
|
||||
table &ast.Table
|
||||
pref &pref.Preferences
|
||||
pub mut:
|
||||
table &ast.Table
|
||||
mods map[string]map[string]Symbol
|
||||
future_register_consts map[string]map[string]map[string]ast.ConstField // mod:file:name:field
|
||||
local_vars map[string]Var
|
||||
local_vars_stack []map[string]Var
|
||||
scope_idx int // this is increased when e.open_scope() is called, decreased when e.close_scope() (and all variables with that scope level deleted)
|
||||
returning bool
|
||||
return_values []Object
|
||||
cur_mod string
|
||||
cur_file string
|
||||
back_trace []string
|
||||
}
|
||||
|
||||
pub struct Var {
|
||||
value Object
|
||||
pub fn (mut e Eval) eval(files []&ast.File) {
|
||||
e.register_symbols(files)
|
||||
// println(files.map(it.path_base))
|
||||
e.run_func(e.mods['main']['main'] or { ast.EmptyStmt{} } as ast.FnDecl)
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) eval(file ast.File, table &ast.Table) string {
|
||||
vpref := &pref.Preferences{}
|
||||
e.table = table
|
||||
mut res := ''
|
||||
e.checker = checker.new_checker(table, vpref)
|
||||
for stmt in file.stmts {
|
||||
res += e.stmt(stmt) + '\n'
|
||||
// first arg is reciever (if method)
|
||||
pub fn (mut e Eval) run_func(func ast.FnDecl, _args ...Object) {
|
||||
e.back_trace << func.name
|
||||
old_mod := e.cur_mod
|
||||
e.cur_mod = func.mod
|
||||
|
||||
old_file := e.cur_file
|
||||
e.cur_file = func.file
|
||||
defer {
|
||||
e.cur_mod = old_mod
|
||||
e.cur_file = old_file
|
||||
e.back_trace.pop()
|
||||
}
|
||||
return res.trim_space()
|
||||
}
|
||||
|
||||
fn print_object(o Object) {
|
||||
match o {
|
||||
int { println(o) }
|
||||
else { println('unknown object') }
|
||||
mut args := _args.clone()
|
||||
if func.params.len != args.len && !func.is_variadic {
|
||||
e.error('mismatched parameter length for $func.name: got `$args.len`, expected `$func.params.len`')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (o Object) str() string {
|
||||
match o {
|
||||
int { return o.str() }
|
||||
else { println('unknown object') }
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn (mut e Eval) stmt(node ast.Stmt) string {
|
||||
match node {
|
||||
ast.AssignStmt {
|
||||
// TODO; replaced VarDecl
|
||||
if func.name in ['print', 'println', 'eprint', 'eprintln'] {
|
||||
s := args[0].string() // stringify because println accepts anything as argument
|
||||
match func.name {
|
||||
'print' {
|
||||
print(s)
|
||||
}
|
||||
'println' {
|
||||
println(s)
|
||||
}
|
||||
'eprint' {
|
||||
eprint(s)
|
||||
}
|
||||
'eprintln' {
|
||||
eprintln(s)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
ast.ExprStmt {
|
||||
o := e.expr(node.expr)
|
||||
print('out: ')
|
||||
print_object(o)
|
||||
return o.str()
|
||||
}
|
||||
// ast.StructDecl {
|
||||
// println('s decl')
|
||||
// }
|
||||
// ast.VarDecl {
|
||||
// e.vars[it.name] = Var{
|
||||
// value: e.expr(it.expr)
|
||||
// }
|
||||
// }
|
||||
else {}
|
||||
}
|
||||
return '>>'
|
||||
}
|
||||
|
||||
fn (mut e Eval) expr(node ast.Expr) Object {
|
||||
match node {
|
||||
ast.IntegerLiteral {
|
||||
return node.val
|
||||
}
|
||||
ast.Ident {
|
||||
print_object(node.value)
|
||||
// Find the variable
|
||||
v := e.vars[node.name]
|
||||
return v.value
|
||||
}
|
||||
ast.InfixExpr {
|
||||
e.checker.infix_expr(mut node)
|
||||
// println('bin $it.op')
|
||||
left := e.expr(node.left) as int
|
||||
right := e.expr(node.right) as int
|
||||
match node.op {
|
||||
.plus { return left + right }
|
||||
.mul { return left * right }
|
||||
else {}
|
||||
} else {
|
||||
e.local_vars_stack << e.local_vars
|
||||
e.local_vars = {}
|
||||
old_scope := e.scope_idx
|
||||
e.scope_idx = 0
|
||||
e.open_scope()
|
||||
// have to do this because of cgen error
|
||||
args__ := if func.is_method { args[1..] } else { args }
|
||||
for i, arg in args__ {
|
||||
e.local_vars[(func.params[i]).name] = Var{
|
||||
val: arg
|
||||
scope_idx: e.scope_idx
|
||||
}
|
||||
}
|
||||
else {}
|
||||
if func.is_method {
|
||||
print(e.back_trace)
|
||||
println(func.receiver.typ - 65536)
|
||||
e.local_vars[func.receiver.name] = Var{
|
||||
val: args[0]
|
||||
scope_idx: e.scope_idx
|
||||
}
|
||||
}
|
||||
e.stmts(func.stmts)
|
||||
e.returning = false
|
||||
e.close_scope()
|
||||
e.scope_idx = old_scope
|
||||
e.local_vars = e.local_vars_stack.pop()
|
||||
}
|
||||
return 0
|
||||
// return Object{}
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) register_symbols(files []&ast.File) {
|
||||
for file in files {
|
||||
// eprintln('registering file: $file.path_base')
|
||||
mod := file.mod.name
|
||||
e.register_symbol_stmts(file.stmts[1..], mod, file.path)
|
||||
|
||||
// eprintln('registered file: $file.path_base')
|
||||
}
|
||||
for mod, const_files in e.future_register_consts {
|
||||
e.cur_mod = mod
|
||||
|
||||
for file, fields in const_files {
|
||||
e.cur_file = file
|
||||
for _, field in fields {
|
||||
e.mods[mod][field.name.all_after_last('.')] = e.expr(field.expr, field.typ)
|
||||
if mod == 'os' && field.name.all_after_last('.') == 'args' {
|
||||
mut res := Array{}
|
||||
res.val << e.pref.out_name.all_after_last('/')
|
||||
for arg in e.pref.run_args {
|
||||
res.val << arg
|
||||
}
|
||||
e.mods[mod][field.name.all_after_last('.')] = Object(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) register_symbol_stmts(stmts []ast.Stmt, mod string, file string) {
|
||||
for stmt in stmts { // first is just module declaration, so ignore
|
||||
e.register_symbol(stmt, mod, file)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) register_symbol(stmt ast.Stmt, mod string, file string) {
|
||||
match stmt {
|
||||
ast.FnDecl {
|
||||
// this mess because c error
|
||||
x := ast.Stmt(stmt)
|
||||
y := Symbol(x as ast.FnDecl)
|
||||
e.mods[mod][stmt.name.all_after_last('.')] = y
|
||||
}
|
||||
ast.Import {} // already handled by builder, TODO: get `as` name
|
||||
ast.StructDecl {} // these are already parsed by the checker into e.table
|
||||
ast.InterfaceDecl {}
|
||||
ast.EnumDecl {}
|
||||
ast.TypeDecl {}
|
||||
ast.GlobalDecl {}
|
||||
ast.HashStmt {}
|
||||
ast.ConstDecl {
|
||||
// evaluate them later since they may use functions defined after this point
|
||||
for field in stmt.fields {
|
||||
e.future_register_consts[mod][file][field.name] = field
|
||||
}
|
||||
}
|
||||
ast.ExprStmt {
|
||||
println('expr')
|
||||
x := stmt.expr
|
||||
match x {
|
||||
ast.IfExpr {
|
||||
if !x.is_comptime {
|
||||
e.error('only comptime ifs are allowed in top level')
|
||||
}
|
||||
for i, branch in x.branches {
|
||||
mut do_if := false
|
||||
println('branch:$branch')
|
||||
match (branch.cond as ast.Ident).name {
|
||||
'windows' {
|
||||
do_if = e.pref.os == .windows
|
||||
}
|
||||
else {
|
||||
e.error('unknown compile time if')
|
||||
}
|
||||
}
|
||||
do_if = do_if || x.branches.len == i + 1
|
||||
if do_if {
|
||||
e.register_symbol_stmts(branch.stmts, mod, file)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown decleration expression statement $x.type_name()')
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unhandled decleration statement $stmt.type_name()')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (e Eval) error(msg string) {
|
||||
util.verror('interpreter', msg)
|
||||
}
|
||||
|
||||
566
vlib/v/eval/expr.v
Normal file
566
vlib/v/eval/expr.v
Normal file
@@ -0,0 +1,566 @@
|
||||
module eval
|
||||
|
||||
import v.ast
|
||||
import v.util
|
||||
import math
|
||||
import strconv
|
||||
|
||||
pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
|
||||
match expr {
|
||||
ast.CallExpr {
|
||||
// println(expr.is_method)
|
||||
// is_method := expr.left.type_name() != 'unknown v.ast.Expr'
|
||||
// println(is_method)
|
||||
if expr.name == 'int' {
|
||||
e.error('methods not supported')
|
||||
}
|
||||
mut args := expr.args.map(e.expr(it.expr, it.typ))
|
||||
if expr.is_method {
|
||||
args.prepend(e.expr(expr.left, expr.receiver_type))
|
||||
}
|
||||
match expr.language {
|
||||
.c {
|
||||
if expr.is_method {
|
||||
e.error('c does not have methods')
|
||||
}
|
||||
match expr.name.all_after('C.') {
|
||||
'write' {
|
||||
return Int{C.write(args[0].int_val(), args[1] as voidptr,
|
||||
args[2].int_val()), 32}
|
||||
}
|
||||
'calloc' {
|
||||
return Ptr{
|
||||
val: vcalloc(int(args[0].int_val() * args[1].int_val()))
|
||||
}
|
||||
}
|
||||
'getcwd' {
|
||||
unsafe {
|
||||
return Ptr{
|
||||
val: C.getcwd((args[0] as Ptr).val as voidptr, args[1].int_val())
|
||||
}
|
||||
}
|
||||
}
|
||||
'memcpy' {
|
||||
unsafe {
|
||||
return Ptr{
|
||||
val: C.memcpy(args[0] as voidptr, args[1] as voidptr,
|
||||
args[2].int_val())
|
||||
}
|
||||
}
|
||||
}
|
||||
// 'printf' {
|
||||
// mut voidptr_args := []voidptr{}
|
||||
// for arg in args[1..] {
|
||||
// // if arg is Int {
|
||||
// // voidptr_args << voidptr(arg.val)
|
||||
// // } else {
|
||||
// voidptr_args << voidptr(&arg)
|
||||
// // }
|
||||
// }
|
||||
// // println((e.local_vars['s'].val as string).str == voidptr_args[1])
|
||||
// println('helo?$voidptr_args')
|
||||
// // println((byteptr(voidptr_args[1])[0]))
|
||||
// x := strconv.v_sprintf(args[0] as string, ...voidptr_args)
|
||||
// // println('helo!')
|
||||
// // println(x.len)
|
||||
// y := C.write(1, x.str, x.len)
|
||||
// println('aft')
|
||||
// return Int{y, 32}
|
||||
// }
|
||||
else {
|
||||
e.error('unknown c function: `$expr.name`')
|
||||
}
|
||||
}
|
||||
}
|
||||
.v {
|
||||
// TODO: Anon functions
|
||||
name := expr.name.all_after_last('.')
|
||||
mod := expr.mod
|
||||
mut func := e.mods[mod][name] or {
|
||||
e.mods['builtin'][name] or { ast.EmptyStmt{} }
|
||||
}
|
||||
|
||||
if func is ast.FnDecl {
|
||||
e.run_func(func as ast.FnDecl, ...args)
|
||||
if e.return_values.len == 1 {
|
||||
return e.return_values[0]
|
||||
} else {
|
||||
return e.return_values
|
||||
}
|
||||
}
|
||||
e.error('unknown function: ${mod}.$name at line $expr.pos.line_nr')
|
||||
}
|
||||
// .js {
|
||||
// e.error('js is not supported')
|
||||
// }
|
||||
else {
|
||||
e.error('$expr.language is not supported as a call expression language')
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.StringLiteral {
|
||||
// escape the escapes
|
||||
mut res := ''
|
||||
mut is_escape := false
|
||||
for c in expr.val {
|
||||
if is_escape {
|
||||
res += e.get_escape(rune(c)).str()
|
||||
} else if c == `\\` {
|
||||
is_escape = true
|
||||
} else {
|
||||
res += rune(c).str()
|
||||
}
|
||||
}
|
||||
|
||||
return Object(res)
|
||||
}
|
||||
ast.IfExpr {
|
||||
if expr.is_expr {
|
||||
e.error('`if` expressions not supported')
|
||||
}
|
||||
|
||||
if expr.is_comptime {
|
||||
for i, branch in expr.branches {
|
||||
mut do_if := false
|
||||
if expr.has_else && i + 1 == expr.branches.len { // else branch
|
||||
do_if = true
|
||||
} else {
|
||||
if branch.cond is ast.Ident {
|
||||
match branch.cond.name {
|
||||
'windows' {
|
||||
do_if = e.pref.os == .windows
|
||||
}
|
||||
'macos' {
|
||||
do_if = e.pref.os == .macos
|
||||
}
|
||||
'linux' {
|
||||
do_if = e.pref.os == .linux
|
||||
}
|
||||
'android' {
|
||||
do_if = e.pref.os == .android
|
||||
}
|
||||
'freebsd' {
|
||||
do_if = e.pref.os == .freebsd
|
||||
}
|
||||
'prealloc' {
|
||||
do_if = e.pref.prealloc
|
||||
}
|
||||
else {
|
||||
e.error('unknown compile time if: $branch.cond.name')
|
||||
}
|
||||
}
|
||||
} else if branch.cond is ast.PostfixExpr {
|
||||
do_if = (branch.cond.expr as ast.Ident).name in e.pref.compile_defines
|
||||
}
|
||||
}
|
||||
if do_if {
|
||||
e.stmts(branch.stmts)
|
||||
break
|
||||
}
|
||||
}
|
||||
return empty
|
||||
} else {
|
||||
for i, b in expr.branches {
|
||||
mut result := e.expr(b.cond, ast.bool_type_idx)
|
||||
|
||||
if expr.has_else && i + 1 == expr.branches.len { // else block
|
||||
e.stmts(b.stmts)
|
||||
break
|
||||
}
|
||||
if result is bool {
|
||||
if result as bool {
|
||||
e.stmts(b.stmts)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
e.error('non-bool expression: $b.cond')
|
||||
}
|
||||
}
|
||||
return empty
|
||||
}
|
||||
}
|
||||
ast.InfixExpr {
|
||||
left := e.expr(expr.left, expr.left_type)
|
||||
right := e.expr(expr.right, expr.right_type)
|
||||
return e.infix_expr(left, right, expr.op, expecting)
|
||||
}
|
||||
ast.IntegerLiteral {
|
||||
// return u64(strconv.parse_uint(expr.val, 0, 64)
|
||||
return i64(strconv.parse_int(expr.val, 0, 64) or {
|
||||
e.error('invalid integer literal: $expr.val')
|
||||
}) // TODO: numbers larger than 2^63 (for u64)
|
||||
}
|
||||
ast.FloatLiteral {
|
||||
return f64(strconv.atof64(expr.val))
|
||||
}
|
||||
ast.BoolLiteral {
|
||||
return expr.val
|
||||
}
|
||||
ast.Ident {
|
||||
match expr.kind {
|
||||
.variable {
|
||||
// println(e.local_vars[expr.name].val.type_name())
|
||||
return e.local_vars[expr.name].val
|
||||
}
|
||||
.constant {
|
||||
return if expr.name.contains('.') {
|
||||
e.mods[expr.name.all_before_last('.')]
|
||||
} else {
|
||||
e.mods[e.cur_mod]
|
||||
}[expr.name.all_after_last('.')] or { ast.EmptyStmt{} } as Object
|
||||
}
|
||||
else {
|
||||
e.error('unknown ident kind for `$expr.name`: $expr.kind')
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.CastExpr {
|
||||
x := e.expr(expr.expr, expr.expr_type)
|
||||
if expr.typ in ast.signed_integer_type_idxs {
|
||||
match x {
|
||||
Uint {
|
||||
return Int{
|
||||
val: i64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
Int {
|
||||
return Int{
|
||||
val: i64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
Float {
|
||||
return Int{
|
||||
val: i64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
i64 {
|
||||
if expecting in ast.signed_integer_type_idxs {
|
||||
return Int{
|
||||
val: x
|
||||
size: i8(e.type_to_size(expecting))
|
||||
}
|
||||
} else {
|
||||
return Uint{
|
||||
val: u64(x)
|
||||
size: i8(e.type_to_size(expecting))
|
||||
}
|
||||
}
|
||||
}
|
||||
f64 {
|
||||
if expecting in ast.signed_integer_type_idxs {
|
||||
return Int{
|
||||
val: i64(x)
|
||||
size: i8(e.type_to_size(expecting))
|
||||
}
|
||||
} else {
|
||||
return Uint{
|
||||
val: u64(x)
|
||||
size: i8(e.type_to_size(expecting))
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
} else if expr.typ in ast.unsigned_integer_type_idxs {
|
||||
match x {
|
||||
Uint {
|
||||
return Uint{
|
||||
val: u64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
Int {
|
||||
return Uint{
|
||||
val: u64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
Float {
|
||||
return Uint{
|
||||
val: u64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
i64 {
|
||||
return Uint{
|
||||
val: u64(x)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
f64 {
|
||||
if expecting in ast.signed_integer_type_idxs {
|
||||
return Int{
|
||||
val: i64(x)
|
||||
size: i8(e.type_to_size(expecting))
|
||||
}
|
||||
} else {
|
||||
return Uint{
|
||||
val: u64(x)
|
||||
size: i8(e.type_to_size(expecting))
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
} else if expr.typ in ast.float_type_idxs {
|
||||
match x {
|
||||
Uint {
|
||||
return Float{
|
||||
val: f64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
Int {
|
||||
return Float{
|
||||
val: f64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
Float {
|
||||
return Float{
|
||||
val: f64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
f64 {
|
||||
return Float{
|
||||
val: x
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
} else if expr.typ in ast.pointer_type_idxs {
|
||||
y := *(x as Ptr).val
|
||||
if expr.typ == ast.byteptr_type_idx {
|
||||
match y {
|
||||
char, voidptr {
|
||||
unsafe {
|
||||
return Ptr{
|
||||
val: (x as Ptr).val
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
} else if expr.typ == ast.voidptr_type_idx {
|
||||
match y {
|
||||
char, Int {
|
||||
unsafe {
|
||||
return Object(voidptr((x as Ptr).val))
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
} else if expr.typ == ast.charptr_type_idx {
|
||||
match y {
|
||||
voidptr, Int {
|
||||
unsafe {
|
||||
return Ptr{
|
||||
val: &char((x as Ptr).val)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if e.table.get_type_symbol(expr.typ).kind in [.interface_, .sum_type] {
|
||||
eprintln(util.formatted_error('warning:', 'sumtype or interface casts return void currently',
|
||||
e.cur_file, expr.pos))
|
||||
} else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
ast.SelectorExpr {
|
||||
exp := e.expr(expr.expr, expr.expr_type)
|
||||
match exp {
|
||||
string {
|
||||
match expr.field_name {
|
||||
'str' {
|
||||
return Ptr{
|
||||
val: exp.str
|
||||
}
|
||||
}
|
||||
'len' {
|
||||
return Int{exp.len, 32}
|
||||
}
|
||||
else {
|
||||
e.error('unknown selector to string: $expr.field_name')
|
||||
}
|
||||
}
|
||||
}
|
||||
Array {
|
||||
match expr.field_name {
|
||||
'len' {
|
||||
return Int{exp.val.len, 32}
|
||||
}
|
||||
else {
|
||||
e.error('unknown selector to array: $expr.field_name')
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown selector expression: $exp.type_name()')
|
||||
}
|
||||
}
|
||||
e.error(exp.str())
|
||||
}
|
||||
ast.ArrayInit {
|
||||
if expr.has_len || expr.has_cap || expr.has_default {
|
||||
if expr.has_len && !expr.has_cap && expr.has_default {
|
||||
return Array{
|
||||
val: []Object{len: int((e.expr(expr.len_expr, 7) as Int).val), init: e.expr(expr.default_expr,
|
||||
expr.elem_type)}
|
||||
}
|
||||
} else if !expr.has_len && expr.has_cap && !expr.has_default {
|
||||
return Array{
|
||||
val: []Object{cap: int((e.expr(expr.cap_expr, 7) as Int).val)}
|
||||
}
|
||||
} else if !expr.has_len && !expr.has_cap && !expr.has_default {
|
||||
return Array{
|
||||
val: []Object{}
|
||||
}
|
||||
} else {
|
||||
e.error('unknown array init combination; len: $expr.has_len, cap: $expr.has_cap, init: $expr.has_default')
|
||||
}
|
||||
}
|
||||
if expr.is_fixed || expr.has_val {
|
||||
e.error('fixed arrays are not supported')
|
||||
}
|
||||
mut res := Array{
|
||||
val: []Object{cap: expr.exprs.len}
|
||||
}
|
||||
|
||||
for i, exp in expr.exprs {
|
||||
res.val << e.expr(exp, expr.expr_types[i])
|
||||
}
|
||||
return res
|
||||
}
|
||||
ast.CharLiteral {
|
||||
if expr.val.len !in [1, 2] {
|
||||
e.error('invalid size of char literal: $expr.val.len')
|
||||
}
|
||||
if expr.val[0] == `\\` { // is an escape
|
||||
return e.get_escape(rune(expr.val[1]))
|
||||
} else {
|
||||
return rune(expr.val[0])
|
||||
}
|
||||
}
|
||||
ast.StructInit {
|
||||
// eprintln('unhandled struct init at line $expr.pos.line_nr')
|
||||
return 'helo'
|
||||
}
|
||||
ast.SizeOf {
|
||||
return Uint{e.type_to_size(expr.typ), 64}
|
||||
}
|
||||
ast.ParExpr {
|
||||
return e.expr(expr.expr, expecting)
|
||||
}
|
||||
ast.PrefixExpr {
|
||||
match expr.op {
|
||||
.amp {
|
||||
x := e.expr(expr.right, expr.right_type)
|
||||
return Ptr{
|
||||
val: &x
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unhandled prefix expression $expr.op')
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.PostfixExpr {
|
||||
match expr.op {
|
||||
.inc {
|
||||
e.add(expr.expr, Int{1, 64})
|
||||
return e.expr(expr.expr, ast.i64_type_idx)
|
||||
}
|
||||
.dec {
|
||||
e.add(expr.expr, Int{-1, 64})
|
||||
return e.expr(expr.expr, ast.i64_type_idx)
|
||||
}
|
||||
else {
|
||||
e.error('unhandled postfix expression $expr.op')
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.StringInterLiteral {
|
||||
mut res := expr.vals[0]
|
||||
|
||||
for i, exp in expr.exprs {
|
||||
res += e.expr(exp, expr.expr_types[i]).string()
|
||||
res += expr.vals[i + 1]
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
else {
|
||||
e.error('unhandled expression $expr.type_name()')
|
||||
}
|
||||
}
|
||||
return empty
|
||||
}
|
||||
|
||||
fn (e Eval) type_to_size(typ ast.Type) u64 {
|
||||
match typ {
|
||||
ast.voidptr_type_idx, ast.byteptr_type_idx, ast.charptr_type_idx {
|
||||
return u64(if e.pref.m64 {
|
||||
64
|
||||
} else {
|
||||
32
|
||||
})
|
||||
}
|
||||
ast.i8_type_idx, ast.i16_type_idx, ast.int_type_idx, ast.i64_type_idx {
|
||||
return u64(math.exp2(f64(typ - 2))) // this formula converts the type number to the bitsize
|
||||
}
|
||||
ast.byte_type_idx, ast.u16_type_idx, ast.u32_type_idx, ast.u64_type_idx {
|
||||
return u64(math.exp2(f64(typ - 6))) // this formula converts the type number to the bitsize
|
||||
}
|
||||
ast.int_literal_type_idx, ast.float_literal_type_idx {
|
||||
return 64
|
||||
}
|
||||
ast.f32_type_idx, ast.f64_type_idx {
|
||||
return u64(math.exp2(f64(typ - 8))) // this formula converts the type number to the bitsize
|
||||
}
|
||||
else {
|
||||
e.error('type_to_size(): unknown type: ${e.table.get_type_symbol(typ).str()}')
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (e Eval) get_escape(r rune) rune {
|
||||
res := match r {
|
||||
`\\` {
|
||||
`\\`
|
||||
}
|
||||
`n` {
|
||||
`\n`
|
||||
}
|
||||
`0` {
|
||||
`\0`
|
||||
}
|
||||
else {
|
||||
`e`
|
||||
}
|
||||
}
|
||||
if res == `e` {
|
||||
e.error('unknown escape: `$r`')
|
||||
}
|
||||
return res
|
||||
}
|
||||
141
vlib/v/eval/gen/infix_gen.v
Normal file
141
vlib/v/eval/gen/infix_gen.v
Normal file
@@ -0,0 +1,141 @@
|
||||
// this file generates ../infix.v
|
||||
module main
|
||||
|
||||
import strings
|
||||
import os
|
||||
|
||||
const (
|
||||
header = 'module eval
|
||||
import v.token
|
||||
import v.ast
|
||||
fn(e Eval)infix_expr(left Object,right Object,op token.Kind,expecting ast.Type)Object{match op{'
|
||||
footer = "else{e.error('unknown infix expression: \$op')}}return empty // should e.error before this anyway
|
||||
}
|
||||
"
|
||||
uk_expect_footer = "else{e.error('unknown infix expectation: \${e.table.get_type_symbol(expecting).str()}')}}"
|
||||
comparison = {
|
||||
'gt': '>'
|
||||
'lt': '<'
|
||||
'eq': '=='
|
||||
'ne': '!='
|
||||
}
|
||||
math_ops = {
|
||||
'plus': '+'
|
||||
'minus': '*'
|
||||
'mul': '+'
|
||||
'div': '+'
|
||||
'right_shift': '>>'
|
||||
'left_shift': '<<'
|
||||
}
|
||||
compound_types = ['Int', 'Uint', 'Float']
|
||||
literal_types = ['i64', 'f64']
|
||||
)
|
||||
|
||||
fn main() {
|
||||
mut b := strings.new_builder(124000)
|
||||
b.write_string(header)
|
||||
|
||||
for enm, op in comparison {
|
||||
b.write_string('.$enm{match left{')
|
||||
for ct in compound_types {
|
||||
b.write_string('$ct {match right{')
|
||||
for ct2 in compound_types {
|
||||
b.write_string('$ct2{return left.val${op}right.val}')
|
||||
}
|
||||
for lt2 in literal_types {
|
||||
b.write_string('$lt2{return left.val${op}right}')
|
||||
}
|
||||
b.write_string("else{e.error('invalid operands to $op: $ct and \$right.type_name()')}}}")
|
||||
}
|
||||
for lt in literal_types {
|
||||
b.write_string('$lt {match right{')
|
||||
for ct2 in compound_types {
|
||||
b.write_string('$ct2{return left${op}right.val}')
|
||||
}
|
||||
for lt2 in literal_types {
|
||||
b.write_string('$lt2{return left${op}right}')
|
||||
}
|
||||
b.write_string("else {e.error('invalid operands to $op: ")
|
||||
b.write_string(if lt == 'i64' { 'int' } else { 'float' })
|
||||
b.write_string(" literal and \$right.type_name()')}}}")
|
||||
}
|
||||
if op in ['==', '!='] {
|
||||
b.write_string('string{match right{string{return left${op}right}else{e.error(\'invalid operands to $op: string and \$right.type_name()\')}}}')
|
||||
}
|
||||
b.write_string("else {e.error('invalid operands to $op: \$left.type_name() and \$right.type_name()')}}}")
|
||||
}
|
||||
for math, op in math_ops {
|
||||
b.write_string('.$math{match left{')
|
||||
for ct in compound_types {
|
||||
if op in ['<<', '>>'] && ct == 'Float' {
|
||||
continue
|
||||
}
|
||||
b.write_string('$ct {match right{')
|
||||
for ct2 in compound_types {
|
||||
if op in ['<<', '>>'] && ct2 == 'Float' {
|
||||
continue
|
||||
}
|
||||
unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
|
||||
b.write_string('$ct2{if expecting in ast.signed_integer_type_idxs{return Int{i64(left.val)+i64(right.val),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{return Uint{u64(left.val)+u64(right.val),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left.val)${op}i64(right.val))$unsafe_end}')
|
||||
if op !in ['<<', '>>'] {
|
||||
b.write_string('else if expecting in ast.float_type_idxs{return Float{f64(left.val)${op}f64(right.val), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{return f64(f64(left.val)${op}f64(right.val))}')
|
||||
}
|
||||
b.write_string(uk_expect_footer)
|
||||
}
|
||||
for lt2 in literal_types {
|
||||
if op in ['<<', '>>'] && lt2 == 'f64' {
|
||||
continue
|
||||
}
|
||||
unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
|
||||
b.write_string('$lt2{if expecting in ast.signed_integer_type_idxs{return Int{i64(left.val)+i64(right),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{return Uint{u64(left.val)+u64(right),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left.val)${op}i64(right))$unsafe_end}')
|
||||
if op !in ['<<', '>>'] {
|
||||
b.write_string('else if expecting in ast.float_type_idxs{return Float{f64(left.val)${op}f64(right), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{return f64(f64(left.val)${op}f64(right))}')
|
||||
}
|
||||
b.write_string(uk_expect_footer)
|
||||
}
|
||||
b.write_string("else {e.error('invalid operands to $op: $ct and \$right.type_name()')}}}")
|
||||
}
|
||||
for lt in literal_types {
|
||||
if op in ['<<', '>>'] && lt == 'f64' {
|
||||
continue
|
||||
}
|
||||
b.write_string('$lt{match right{')
|
||||
for ct2 in compound_types {
|
||||
if op in ['<<', '>>'] && ct2 == 'Float' {
|
||||
continue
|
||||
}
|
||||
unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
|
||||
b.write_string('$ct2{if expecting in ast.signed_integer_type_idxs{return Int{i64(left)+i64(right.val),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{return Uint{u64(left)+u64(right.val),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left)${op}i64(right.val))$unsafe_end}')
|
||||
if op !in ['<<', '>>'] {
|
||||
b.write_string('else if expecting in ast.float_type_idxs{return Float{f64(left)${op}f64(right.val), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{return f64(f64(left)${op}f64(right.val))}')
|
||||
}
|
||||
b.write_string(uk_expect_footer)
|
||||
}
|
||||
for lt2 in literal_types {
|
||||
if op in ['<<', '>>'] && lt2 == 'f64' {
|
||||
continue
|
||||
}
|
||||
unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
|
||||
b.write_string('$lt2{if expecting in ast.signed_integer_type_idxs{return Int{i64(left)+i64(right),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{return Uint{u64(left)+u64(right),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left)${op}i64(right))$unsafe_end}')
|
||||
if op !in ['<<', '>>'] {
|
||||
b.write_string('else if expecting in ast.float_type_idxs{return Float{f64(left)${op}f64(right), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{return f64(f64(left)${op}f64(right))}')
|
||||
}
|
||||
b.write_string(uk_expect_footer)
|
||||
}
|
||||
b.write_string("else {e.error('invalid operands to $op: ")
|
||||
b.write_string(if lt == 'i64' { 'int' } else { 'float' })
|
||||
b.write_string(" literal and \$right.type_name()')}}}")
|
||||
}
|
||||
b.write_string("else {e.error('invalid operands to $op: \$left.type_name() and \$right.type_name()')}}}")
|
||||
}
|
||||
|
||||
b.write_string(footer)
|
||||
|
||||
path := @FILE.all_before(@FILE.all_after_last('/')) + '../infix.v'
|
||||
os.write_file(path, b.str()) or { panic(err) }
|
||||
res := os.execute(@VEXE + ' fmt -w ' + path)
|
||||
if res.exit_code != 0 {
|
||||
eprintln('v fmt failed!')
|
||||
panic(res.output)
|
||||
}
|
||||
}
|
||||
2211
vlib/v/eval/infix.v
Normal file
2211
vlib/v/eval/infix.v
Normal file
File diff suppressed because it is too large
Load Diff
150
vlib/v/eval/object.v
Normal file
150
vlib/v/eval/object.v
Normal file
@@ -0,0 +1,150 @@
|
||||
module eval
|
||||
|
||||
const empty = Void{}
|
||||
|
||||
// NB: i64 is an int_literal, NOT an i64 (same with f64)
|
||||
type Object = Array
|
||||
| Float
|
||||
| Int
|
||||
| Ptr
|
||||
| Uint
|
||||
| Void
|
||||
| []Object
|
||||
| bool
|
||||
| byte
|
||||
| char
|
||||
| f64
|
||||
| i64
|
||||
| rune
|
||||
| string
|
||||
| voidptr
|
||||
|
||||
// string is the same as the autogenerated str() methods
|
||||
pub fn (o Object) string() string {
|
||||
match o {
|
||||
byte {
|
||||
panic('error: byte should only be used for &byte')
|
||||
}
|
||||
bool {
|
||||
return o.str()
|
||||
}
|
||||
i64 { // int_literal
|
||||
return o.str()
|
||||
}
|
||||
f64 { // float_literal
|
||||
return o.str()
|
||||
}
|
||||
Int {
|
||||
return o.val.str()
|
||||
}
|
||||
Uint {
|
||||
return o.val.str()
|
||||
}
|
||||
Float {
|
||||
return o.val.str()
|
||||
}
|
||||
string {
|
||||
return o
|
||||
}
|
||||
Void {
|
||||
return ''
|
||||
}
|
||||
[]Object {
|
||||
mut res := '('
|
||||
for i, obj in o {
|
||||
res += obj.str()
|
||||
if i + 1 != o.len {
|
||||
res += ', '
|
||||
}
|
||||
}
|
||||
return res + ')'
|
||||
}
|
||||
voidptr {
|
||||
return o.str()
|
||||
}
|
||||
char {
|
||||
return int(o).str()
|
||||
}
|
||||
rune {
|
||||
return o.str()
|
||||
}
|
||||
Array {
|
||||
mut res := '['
|
||||
for i, val in o.val {
|
||||
res += val.string()
|
||||
if i + 1 != o.val.len {
|
||||
res += ', '
|
||||
}
|
||||
}
|
||||
return res + ']'
|
||||
}
|
||||
Ptr {
|
||||
return o.val.str()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (o Object) int_val() i64 {
|
||||
match o {
|
||||
Int {
|
||||
return o.val
|
||||
}
|
||||
Uint {
|
||||
return i64(o.val)
|
||||
}
|
||||
i64 {
|
||||
return o
|
||||
}
|
||||
else {
|
||||
panic('Object.int_val(): not an int')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (o Object) float_val() f64 {
|
||||
match o {
|
||||
Float {
|
||||
return o.val
|
||||
}
|
||||
f64 {
|
||||
return o
|
||||
}
|
||||
else {
|
||||
panic('Object.float_val(): not a float')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Void {}
|
||||
|
||||
pub struct Int {
|
||||
pub mut:
|
||||
val i64
|
||||
size i8 // 8/16/32/64
|
||||
}
|
||||
|
||||
pub struct Uint {
|
||||
pub mut:
|
||||
val u64
|
||||
size i8 // 8/16/32/64
|
||||
}
|
||||
|
||||
pub struct Float {
|
||||
pub mut:
|
||||
val f64
|
||||
size i8 // 8/16/32/64
|
||||
}
|
||||
|
||||
pub struct Array {
|
||||
pub mut:
|
||||
val []Object
|
||||
}
|
||||
|
||||
pub struct Ptr {
|
||||
val &Object
|
||||
}
|
||||
|
||||
// override the autogenerated str, since it does not work
|
||||
fn (p Ptr) str() string {
|
||||
return u64(p.val).str()
|
||||
}
|
||||
93
vlib/v/eval/stmt.v
Normal file
93
vlib/v/eval/stmt.v
Normal file
@@ -0,0 +1,93 @@
|
||||
module eval
|
||||
|
||||
import v.ast
|
||||
|
||||
pub fn (mut e Eval) stmts(stmts []ast.Stmt) {
|
||||
e.open_scope()
|
||||
for stmt in stmts {
|
||||
e.stmt(stmt)
|
||||
if e.returning {
|
||||
break
|
||||
}
|
||||
}
|
||||
e.close_scope()
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) stmt(stmt ast.Stmt) {
|
||||
match stmt {
|
||||
ast.ExprStmt {
|
||||
e.expr(stmt.expr, stmt.typ)
|
||||
}
|
||||
ast.AssignStmt {
|
||||
// if stmt.left.len != 1 {
|
||||
// e.error('multiple values assignments are not supported')
|
||||
// }
|
||||
mut rights := []Object{}
|
||||
for i, right in stmt.right {
|
||||
rights << e.expr(right, stmt.right_types[i])
|
||||
}
|
||||
if rights[0] is []Object { // needs to be unpacked
|
||||
e.error('multiple assignment from function is not supported')
|
||||
}
|
||||
match stmt.op {
|
||||
.decl_assign {
|
||||
for i, left in stmt.left {
|
||||
e.set(left, rights[i], true, stmt.left_types[i])
|
||||
}
|
||||
}
|
||||
.assign {
|
||||
for i, left in stmt.left {
|
||||
e.set(left, rights[i], false, stmt.left_types[i])
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown assign statment: $stmt.op')
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.Return {
|
||||
e.returning = true
|
||||
e.return_values = []
|
||||
for i, expr in stmt.exprs {
|
||||
e.return_values << e.expr(expr, stmt.types[i])
|
||||
}
|
||||
}
|
||||
ast.ForInStmt {
|
||||
if !stmt.is_range {
|
||||
e.error('only range for in statements are supported')
|
||||
}
|
||||
if stmt.key_var != '' {
|
||||
e.error('keys are not supported in for in statements')
|
||||
}
|
||||
e.open_scope()
|
||||
underscore := stmt.val_var == '_'
|
||||
if !underscore {
|
||||
e.set(ast.Ident{ name: stmt.val_var, scope: 0 }, Int{-1, 32}, true, stmt.val_type)
|
||||
}
|
||||
for i in (e.expr(stmt.cond, ast.int_type_idx) as Int).val .. (e.expr(stmt.high,
|
||||
ast.int_type_idx) as Int).val {
|
||||
if !underscore {
|
||||
e.set(ast.Ident{ name: stmt.val_var, scope: 0 }, Int{i, 32}, false,
|
||||
stmt.val_type)
|
||||
}
|
||||
e.stmts(stmt.stmts)
|
||||
}
|
||||
e.close_scope()
|
||||
}
|
||||
ast.ForStmt {
|
||||
for {
|
||||
should_break := e.expr(stmt.cond, ast.bool_type_idx)
|
||||
if !(should_break as bool) {
|
||||
break
|
||||
}
|
||||
e.stmts(stmt.stmts)
|
||||
}
|
||||
}
|
||||
ast.Block {
|
||||
e.stmts(stmt.stmts)
|
||||
}
|
||||
else {
|
||||
e.error('unhandled statement $stmt.type_name()')
|
||||
}
|
||||
}
|
||||
}
|
||||
68
vlib/v/eval/var.v
Normal file
68
vlib/v/eval/var.v
Normal file
@@ -0,0 +1,68 @@
|
||||
module eval
|
||||
|
||||
import v.ast
|
||||
|
||||
pub struct Var {
|
||||
pub mut:
|
||||
val Object
|
||||
pub:
|
||||
scope_idx int
|
||||
typ ast.Type
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) open_scope() {
|
||||
e.scope_idx++
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) close_scope() {
|
||||
e.scope_idx--
|
||||
for name, var in e.local_vars {
|
||||
if var.scope_idx > e.scope_idx {
|
||||
e.local_vars.delete(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) set(expr ast.Expr, val Object, init bool, typ ast.Type) {
|
||||
match expr {
|
||||
ast.Ident {
|
||||
if init {
|
||||
e.local_vars[expr.name] = Var{
|
||||
val: val
|
||||
scope_idx: e.scope_idx
|
||||
typ: typ
|
||||
}
|
||||
} else {
|
||||
e.local_vars[expr.name].val = val
|
||||
}
|
||||
}
|
||||
ast.IndexExpr {
|
||||
panic('>>$expr.pos, $e.cur_file')
|
||||
|
||||
// if init {
|
||||
// e.error('index init assignment')
|
||||
// } else {
|
||||
// mut x := (e.local_vars[(expr.left as ast.Ident).name].val)
|
||||
// if x is Array {
|
||||
// x.val[(e.expr(expr.index, ast.int_type_idx) as Int).val] = val
|
||||
// }
|
||||
// }
|
||||
}
|
||||
else {
|
||||
panic('unknown left value to assign statment: $expr.type_name()')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// val and expr must be both numeric types, or both string
|
||||
pub fn (mut e Eval) add(expr ast.Expr, val Object) {
|
||||
match expr {
|
||||
ast.Ident {
|
||||
e.local_vars[expr.name].val = e.infix_expr(e.local_vars[expr.name].val, val,
|
||||
.plus, e.local_vars[expr.name].typ)
|
||||
}
|
||||
else {
|
||||
panic('unknown left value to add statment: $expr.type_name()')
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user