basic One2C code gen
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,6 +6,8 @@ onev
|
|||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
*.dll
|
*.dll
|
||||||
|
*.c
|
||||||
|
*.one
|
||||||
|
|
||||||
# Ignore binary output folders
|
# Ignore binary output folders
|
||||||
bin/
|
bin/
|
||||||
|
|||||||
132
generator.v
Normal file
132
generator.v
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
import strings
|
||||||
|
import os
|
||||||
|
|
||||||
|
struct Generator {
|
||||||
|
mut:
|
||||||
|
out strings.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Generator) get_c_type(typ string) string {
|
||||||
|
c_type := if typ == 'real' { 'double' } else { typ }
|
||||||
|
return c_type
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Generator) gen_stmt(stmt Stmt) {
|
||||||
|
match stmt {
|
||||||
|
VarDecl {
|
||||||
|
c_type := g.get_c_type(stmt.type)
|
||||||
|
g.out.write_string('${c_type} ${stmt.name} = ')
|
||||||
|
g.gen_expr(stmt.value)
|
||||||
|
g.out.writeln(';')
|
||||||
|
}
|
||||||
|
ExprStmt {
|
||||||
|
g.gen_expr(stmt.expr)
|
||||||
|
g.out.writeln(';')
|
||||||
|
}
|
||||||
|
ReturnStmt {
|
||||||
|
g.out.write_string('return ')
|
||||||
|
g.gen_expr(stmt.expr)
|
||||||
|
g.out.writeln(';')
|
||||||
|
}
|
||||||
|
Block {
|
||||||
|
g.out.writeln('{')
|
||||||
|
for inner_stmt in stmt.stmts {
|
||||||
|
g.gen_stmt(inner_stmt)
|
||||||
|
}
|
||||||
|
g.out.writeln('}')
|
||||||
|
}
|
||||||
|
FuncDecl {
|
||||||
|
c_type := g.get_c_type(stmt.ret_type)
|
||||||
|
g.out.write_string('${c_type} ${stmt.name}() ')
|
||||||
|
g.gen_stmt(stmt.block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Generator) gen_expr(expr Expr) {
|
||||||
|
match expr {
|
||||||
|
RealLiteral {
|
||||||
|
g.out.write_string(expr.val.str())
|
||||||
|
}
|
||||||
|
IntegerLiteral {
|
||||||
|
g.out.write_string(expr.val.str())
|
||||||
|
}
|
||||||
|
BoolLiteral {
|
||||||
|
g.out.write_string(expr.val.str())
|
||||||
|
}
|
||||||
|
Variable {
|
||||||
|
g.out.write_string(expr.name)
|
||||||
|
}
|
||||||
|
UnaryExpr {
|
||||||
|
// Handle postfix/prefix logic if necessary
|
||||||
|
g.out.write_string('${expr.ident}${expr.op}')
|
||||||
|
}
|
||||||
|
BinaryExpr {
|
||||||
|
g.out.write_string('(')
|
||||||
|
g.gen_expr(expr.left)
|
||||||
|
g.out.write_string(' ${expr.op} ')
|
||||||
|
g.gen_expr(expr.right)
|
||||||
|
g.out.write_string(')')
|
||||||
|
}
|
||||||
|
PrintExpr {
|
||||||
|
g.out.write_string('printf(\"%')
|
||||||
|
format := match expr.type {
|
||||||
|
'int' {'d'}
|
||||||
|
'real' {'lf'}
|
||||||
|
'bool' {'d'}
|
||||||
|
else {panic("Tried printing illegal type")}
|
||||||
|
}
|
||||||
|
g.out.write_string('${format}\\n\", ')
|
||||||
|
g.gen_expr(expr.expr)
|
||||||
|
g.out.write_string(')')
|
||||||
|
}
|
||||||
|
TypeCast {
|
||||||
|
c_type := g.get_c_type(expr.type)
|
||||||
|
g.out.write_string('(${c_type})')
|
||||||
|
g.gen_expr(expr.expr)
|
||||||
|
}
|
||||||
|
else {panic('unimplemented ${expr}')}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Generator) gen_c(program []Stmt) string {
|
||||||
|
g.out.writeln('#include <stdio.h>')
|
||||||
|
g.out.writeln('#include <stdbool.h>') // For your 'bool' types [cite: 4]
|
||||||
|
for stmt in program {
|
||||||
|
g.gen_stmt(stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return g.out.str()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_with_clang(c_code string, output_name string, keep_c bool) {
|
||||||
|
// 1. Write the C code to a temporary file
|
||||||
|
c_file := 'middle_c.c'
|
||||||
|
os.write_file(c_file, c_code) or {
|
||||||
|
eprintln('Failed to write C file: $err')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Construct the clang command
|
||||||
|
// We'll use -O2 for optimization and -o to specify the output binary
|
||||||
|
clang_cmd := 'clang ${c_file} -o ${output_name} -O2'
|
||||||
|
|
||||||
|
println('Executing: ${clang_cmd}')
|
||||||
|
|
||||||
|
// 3. Run the command
|
||||||
|
result := os.execute(clang_cmd)
|
||||||
|
|
||||||
|
if result.exit_code != 0 {
|
||||||
|
eprintln('Clang Compilation Failed:')
|
||||||
|
eprintln(result.output)
|
||||||
|
} else {
|
||||||
|
println('Compilation successful! Binary created: $output_name')
|
||||||
|
// Optional: Remove the temporary C file
|
||||||
|
if !keep_c {
|
||||||
|
os.rm(c_file) or { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
lexer.v
4
lexer.v
@@ -11,6 +11,7 @@ enum TokenType as u8 {
|
|||||||
kw_break
|
kw_break
|
||||||
kw_fn
|
kw_fn
|
||||||
kw_return
|
kw_return
|
||||||
|
kw_print
|
||||||
integer
|
integer
|
||||||
real
|
real
|
||||||
boolean
|
boolean
|
||||||
@@ -99,6 +100,7 @@ fn toktype_from_kw(kw string) TokenType {
|
|||||||
'fn' {.kw_fn}
|
'fn' {.kw_fn}
|
||||||
'return' {.kw_return}
|
'return' {.kw_return}
|
||||||
'true', 'false' {.boolean}
|
'true', 'false' {.boolean}
|
||||||
|
'print' {.kw_print}
|
||||||
else {.unknown}
|
else {.unknown}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,7 +116,7 @@ fn is_real(str string) bool {
|
|||||||
|
|
||||||
fn is_keyword(str string) bool {
|
fn is_keyword(str string) bool {
|
||||||
return [
|
return [
|
||||||
"void", "int", "real", "bool", "if", "else", "for", "break", "fn", "return", "let", "true", "false"
|
"void", "int", "real", "bool", "if", "else", "for", "break", "fn", "return", "let", "true", "false", "print"
|
||||||
].contains(str)
|
].contains(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14
main.v
14
main.v
@@ -1,9 +1,10 @@
|
|||||||
module main
|
module main
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import strings
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
content := os.read_file("test.one") or { return }
|
content := os.read_file("source.one") or { return }
|
||||||
println("---------\n" + content + "---------")
|
println("---------\n" + content + "---------")
|
||||||
tokens := lex(content) or { return }
|
tokens := lex(content) or { return }
|
||||||
println("-- TOK --")
|
println("-- TOK --")
|
||||||
@@ -17,4 +18,15 @@ fn main() {
|
|||||||
|
|
||||||
println("-- AST --")
|
println("-- AST --")
|
||||||
println(statements)
|
println(statements)
|
||||||
|
|
||||||
|
mut generator := Generator{
|
||||||
|
out: strings.new_builder(100)
|
||||||
|
}
|
||||||
|
|
||||||
|
out_c := generator.gen_c(statements)
|
||||||
|
|
||||||
|
println("--- C ---")
|
||||||
|
println(out_c)
|
||||||
|
compile_with_clang(out_c, 'test', true)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
76
parser.v
76
parser.v
@@ -2,6 +2,28 @@ module main
|
|||||||
|
|
||||||
import term
|
import term
|
||||||
|
|
||||||
|
// ------------------------------------------- Precedence
|
||||||
|
|
||||||
|
enum Precedence {
|
||||||
|
lowest
|
||||||
|
assignment // = , +=, -=
|
||||||
|
comparison // ==, !=, <, >
|
||||||
|
sum // +, -
|
||||||
|
product // *, /
|
||||||
|
prefix // -x, !x
|
||||||
|
call // function()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p Parser) get_precedence(tok_type TokenType) Precedence {
|
||||||
|
return match tok_type {
|
||||||
|
.equals, .plus_eq, .minus_eq, .star_eq, .slash_eq { .assignment }
|
||||||
|
.eq_eq, .not_eq, .less_eq, .greater_eq { .comparison }
|
||||||
|
.plus, .minus { .sum }
|
||||||
|
.star, .slash { .product }
|
||||||
|
else { .lowest }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------- Symbol Table
|
// ------------------------------------------- Symbol Table
|
||||||
|
|
||||||
type SymbolInfo = VarSymbolInfo | FuncSymbolInfo
|
type SymbolInfo = VarSymbolInfo | FuncSymbolInfo
|
||||||
@@ -70,7 +92,7 @@ fn (mut s SymbolTable) is_in_global_scope() bool {
|
|||||||
|
|
||||||
// ------------------------------------------- Expressions
|
// ------------------------------------------- Expressions
|
||||||
|
|
||||||
type Expr = VoidExpr | UnaryExpr | BinaryExpr | IntegerLiteral | RealLiteral | BoolLiteral | Variable | TypeExpr | Function | TypeCast | ParenExpr
|
type Expr = VoidExpr | UnaryExpr | BinaryExpr | IntegerLiteral | RealLiteral | BoolLiteral | Variable | TypeExpr | Function | TypeCast | ParenExpr | PrintExpr
|
||||||
|
|
||||||
struct VoidExpr {}
|
struct VoidExpr {}
|
||||||
|
|
||||||
@@ -118,6 +140,11 @@ struct ParenExpr {
|
|||||||
expr Expr
|
expr Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PrintExpr {
|
||||||
|
expr Expr
|
||||||
|
type string
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------- Statements
|
// ------------------------------------------- Statements
|
||||||
|
|
||||||
type Stmt = VarDecl | ExprStmt | ReturnStmt | Block | FuncDecl
|
type Stmt = VarDecl | ExprStmt | ReturnStmt | Block | FuncDecl
|
||||||
@@ -139,7 +166,7 @@ struct ExprStmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ReturnStmt {
|
struct ReturnStmt {
|
||||||
value Expr
|
expr Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Block {
|
struct Block {
|
||||||
@@ -210,20 +237,20 @@ fn (mut p Parser) parse_primary() Expr {
|
|||||||
.identifier {p.parse_ident(token.text)}
|
.identifier {p.parse_ident(token.text)}
|
||||||
.type {p.parse_type(token.text)}
|
.type {p.parse_type(token.text)}
|
||||||
.lparen {p.parse_paren()}
|
.lparen {p.parse_paren()}
|
||||||
|
.kw_print {p.parse_print()}
|
||||||
else {parse_error("Unexpected Token")}
|
else {parse_error("Unexpected Token")}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut p Parser) parse_expr() Expr {
|
fn (mut p Parser) parse_expr(prec Precedence) Expr {
|
||||||
mut left := p.parse_primary()
|
mut expr := p.parse_primary()
|
||||||
|
|
||||||
match p.peek().type {
|
for int(prec) < int(p.get_precedence(p.peek().type)) {
|
||||||
.plus, .minus, .star, .slash, .equals, .eq_eq, .not_eq, .less_eq, .greater_eq,
|
op_tok := p.next()
|
||||||
.plus_eq, .minus_eq, .star_eq, .slash_eq {
|
expr = p.parse_binary(expr, op_tok.text, p.get_precedence(op_tok.type))
|
||||||
return p.parse_binary(left, p.peek().text)
|
|
||||||
}
|
|
||||||
else {return left}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut p Parser) parse_ident(ident string) Expr {
|
fn (mut p Parser) parse_ident(ident string) Expr {
|
||||||
@@ -233,10 +260,19 @@ fn (mut p Parser) parse_ident(ident string) Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut p Parser) parse_binary(left Expr, op string) BinaryExpr {
|
fn (mut p Parser) parse_print() PrintExpr {
|
||||||
p.next()
|
p.expect(.lparen)
|
||||||
right := p.parse_expr()
|
expr := p.parse_expr(.lowest)
|
||||||
|
p.expect(.rparen)
|
||||||
|
return PrintExpr{expr: expr, type: p.get_expr_type(expr)}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut p Parser) parse_binary(left Expr, op string, prec Precedence) BinaryExpr {
|
||||||
|
//p.next()
|
||||||
|
right := p.parse_expr(prec)
|
||||||
binary_expr := BinaryExpr{left, op, right}
|
binary_expr := BinaryExpr{left, op, right}
|
||||||
|
|
||||||
|
|
||||||
if !p.is_op_valid_for_type(p.get_expr_type(left), op) {
|
if !p.is_op_valid_for_type(p.get_expr_type(left), op) {
|
||||||
parse_error("Illegal operation ${op} for type ${p.get_expr_type(left)}")
|
parse_error("Illegal operation ${op} for type ${p.get_expr_type(left)}")
|
||||||
}
|
}
|
||||||
@@ -248,7 +284,7 @@ fn (mut p Parser) parse_type(type string) Expr {
|
|||||||
|
|
||||||
if p.peek().type == .lparen {
|
if p.peek().type == .lparen {
|
||||||
p.next()
|
p.next()
|
||||||
expr := p.parse_expr()
|
expr := p.parse_expr(.lowest)
|
||||||
p.expect(.rparen)
|
p.expect(.rparen)
|
||||||
|
|
||||||
return TypeCast {
|
return TypeCast {
|
||||||
@@ -261,7 +297,7 @@ fn (mut p Parser) parse_type(type string) Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut p Parser) parse_paren() ParenExpr {
|
fn (mut p Parser) parse_paren() ParenExpr {
|
||||||
expr := p.parse_expr()
|
expr := p.parse_expr(.lowest)
|
||||||
p.expect(.rparen)
|
p.expect(.rparen)
|
||||||
return ParenExpr{expr: expr}
|
return ParenExpr{expr: expr}
|
||||||
}
|
}
|
||||||
@@ -356,7 +392,7 @@ fn (mut p Parser) parse_var_decl() VarDecl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p.expect(.equals)
|
p.expect(.equals)
|
||||||
val := p.parse_expr()
|
val := p.parse_expr(.lowest)
|
||||||
|
|
||||||
if type_tok.text == 'void' {
|
if type_tok.text == 'void' {
|
||||||
parse_error("Cannot declare a variable of type void")
|
parse_error("Cannot declare a variable of type void")
|
||||||
@@ -402,9 +438,9 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
|
|||||||
return_stmts := p.get_return_stmts_recursive(block)
|
return_stmts := p.get_return_stmts_recursive(block)
|
||||||
|
|
||||||
for return_stmt in return_stmts {
|
for return_stmt in return_stmts {
|
||||||
if p.get_expr_type(return_stmt.value) != type_tok.text {
|
if p.get_expr_type(return_stmt.expr) != type_tok.text {
|
||||||
parse_error("Mismatch between declared return type (${type_tok.text}) \
|
parse_error("Mismatch between declared return type (${type_tok.text}) \
|
||||||
and actual return type (${p.get_expr_type(return_stmt.value)})")
|
and actual return type (${p.get_expr_type(return_stmt.expr)})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,12 +473,12 @@ fn (mut p Parser) parse_return_stmt() ReturnStmt {
|
|||||||
p.expect(.semicolon)
|
p.expect(.semicolon)
|
||||||
}
|
}
|
||||||
return ReturnStmt {
|
return ReturnStmt {
|
||||||
value: expr
|
expr: expr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut p Parser) parse_expr_stmt() ExprStmt {
|
fn (mut p Parser) parse_expr_stmt() ExprStmt {
|
||||||
expr := p.parse_expr()
|
expr := p.parse_expr(.lowest)
|
||||||
p.expect(.semicolon)
|
p.expect(.semicolon)
|
||||||
|
|
||||||
return ExprStmt {
|
return ExprStmt {
|
||||||
|
|||||||
Reference in New Issue
Block a user