231 lines
4.6 KiB
V
231 lines
4.6 KiB
V
module ast
|
|
import constants { Primitives }
|
|
import intern { InternPool }
|
|
import token as _ { Tokenizer, Token, TokenType }
|
|
|
|
pub struct VuaNil {}
|
|
pub struct VuaBool {
|
|
value bool
|
|
}
|
|
pub struct VuaNumber {
|
|
value string
|
|
}
|
|
pub struct VuaString {
|
|
value string // string interning
|
|
}
|
|
pub struct VuaTable {
|
|
keys []u32 // string interning
|
|
values []u32 // index in all tables
|
|
}
|
|
pub struct VuaFunction {
|
|
name u32 // string interning
|
|
args []u32 // string interning
|
|
body []Token // slice of tokens representing the function body
|
|
}
|
|
|
|
pub type VuaValue =
|
|
VuaNil
|
|
| VuaBool
|
|
| VuaNumber
|
|
| VuaString
|
|
| VuaTable
|
|
| VuaFunction
|
|
|
|
@[heap]
|
|
pub struct Environment {
|
|
mut:
|
|
bools []VuaBool
|
|
ints []VuaNumber
|
|
decimals []VuaNumber
|
|
strings []VuaString
|
|
tables []VuaTable
|
|
functions map[string]VuaFunction
|
|
str_pool &InternPool = &InternPool{}
|
|
pub mut:
|
|
types map[string]Type = {
|
|
"string" : TypeAlias{"string", .string},
|
|
"number" : TypeAlias{"number", .number},
|
|
"decimal" : TypeAlias{"decimal", .decimal},
|
|
"bool" : TypeAlias{"bool", .bool},
|
|
"table" : TypeAlias{"table", .table},
|
|
"function": TypeAlias{"function", .function},
|
|
}
|
|
vars map[string]Var
|
|
}
|
|
|
|
/// index for bools, ints, decimals, strings, etc
|
|
pub type EnvironmentIndex = int
|
|
|
|
pub struct Var {
|
|
name string
|
|
type string
|
|
value EnvironmentIndex
|
|
}
|
|
|
|
pub struct TypeAlias {
|
|
name string
|
|
alias Primitives
|
|
}
|
|
|
|
pub fn (t TypeAlias) str() string {
|
|
return t.name
|
|
}
|
|
|
|
pub struct StructDefinition {
|
|
name string
|
|
fields map[string]EnvironmentIndex
|
|
}
|
|
|
|
pub struct UnionDefinition {
|
|
name string
|
|
fields map[string]EnvironmentIndex
|
|
}
|
|
|
|
pub type Type =
|
|
TypeAlias
|
|
| StructDefinition
|
|
| UnionDefinition
|
|
|
|
@[heap]
|
|
pub struct Parser {
|
|
input string
|
|
mut:
|
|
stack []Token
|
|
frame u32
|
|
max_pos u32
|
|
pub mut:
|
|
env &Environment
|
|
}
|
|
|
|
/// advances once
|
|
pub fn (mut p Parser) next() ?Token {
|
|
panic("TODO")
|
|
}
|
|
|
|
/// rollback the parser to the previous token
|
|
pub fn (mut p Parser) rollback() {
|
|
panic("TODO")
|
|
}
|
|
|
|
/// extracts the string from the token starting position, might need re parsing
|
|
pub fn (mut p Parser) save_stash(start u32) string {
|
|
panic("TODO")
|
|
}
|
|
|
|
/// expect a string and a token to match against, if either fails, it'll fail
|
|
pub fn (mut p Parser) expect(s string, tag TokenType) ?(string, Token) {
|
|
panic("TODO")
|
|
}
|
|
|
|
/// any token of type .keyword, returns the extracted keyword
|
|
pub fn (mut p Parser) keyword() ?string {
|
|
panic("TODO")
|
|
}
|
|
|
|
/// any token of type .identifier, returns the extracted identifier
|
|
pub fn (mut p Parser) identifier() ?string {
|
|
panic("TODO")
|
|
}
|
|
|
|
pub fn (mut p Parser) expr() !VuaValue {
|
|
if token := p.next() {
|
|
match token.tag {
|
|
.identifier {
|
|
id := p.save_stash(token.start)
|
|
if var := p.env.vars[id] {
|
|
eprintln(var)
|
|
} else {
|
|
return error("identifier error")
|
|
}
|
|
panic("invalid code path")
|
|
}
|
|
.nil, .true, .false, .number, .decimal, .string {
|
|
return error("expression error")
|
|
}
|
|
.keyword {
|
|
p.rollback()
|
|
return p.keyword_expr()
|
|
}
|
|
else {
|
|
p.rollback()
|
|
return error("unsupported type")
|
|
}
|
|
}
|
|
}
|
|
|
|
return error("impossible")
|
|
}
|
|
|
|
pub fn (mut p Parser) keyword_expr() !VuaValue {
|
|
keyword := p.keyword() or {
|
|
return error("invalid keyword")
|
|
}
|
|
|
|
match keyword {
|
|
"local" {
|
|
lhs := p.identifier() or {
|
|
return error("invalid identifier")
|
|
}
|
|
|
|
type_name := p.identifier()
|
|
|
|
if type_name == none {
|
|
p.rollback()
|
|
}
|
|
|
|
p.expect("=", .operator) or {
|
|
return error("invalid assignment")
|
|
}
|
|
|
|
rhs := p.next() or {
|
|
return error("invalid right hand side of assignment")
|
|
}
|
|
|
|
match rhs.tag {
|
|
.number, .decimal, .string, .true, .false, .nil {
|
|
p.env.vars[p.env.str_pool.intern(lhs)] = Var{
|
|
name: lhs,
|
|
type: match rhs.tag {
|
|
.true, .false { "bool" }
|
|
else { rhs.tag.str() }
|
|
},
|
|
value: rhs.start
|
|
}
|
|
|
|
match rhs.tag {
|
|
.true {
|
|
vbool := p.input[rhs.start..rhs.start + 4]
|
|
assert vbool == "true"
|
|
return VuaValue(VuaBool{true})
|
|
}
|
|
.false {
|
|
vbool := p.input[rhs.start..rhs.start + 6]
|
|
assert vbool == "false"
|
|
return VuaValue(VuaBool{false})
|
|
}
|
|
.number, .decimal {
|
|
vnum := p.save_stash(rhs.start)
|
|
return VuaValue(VuaNumber{vnum})
|
|
}
|
|
.string {
|
|
// might be impossible with tokens pre allocated
|
|
vstr := p.save_stash(rhs.start)
|
|
// dirty trick
|
|
return VuaValue(VuaString{vstr[1..vstr.len-1]})
|
|
}
|
|
.nil { return VuaValue(VuaNil{}) }
|
|
else { return error("failed rhs inference") }
|
|
}
|
|
}
|
|
else {
|
|
return error("invalid rhs type")
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
return error("unsupported keyword")
|
|
}
|
|
}
|
|
panic('No expression found')
|
|
}
|