basic One2C code gen

This commit is contained in:
uan
2026-02-04 21:04:58 +01:00
parent 541d3c7c7f
commit ff30ef8153
7 changed files with 206 additions and 25 deletions

2
.gitignore vendored
View File

@@ -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
View 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 { }
}
}
}

View File

@@ -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
View File

@@ -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)
} }

View File

@@ -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 {

BIN
test Executable file

Binary file not shown.

View File

@@ -1,3 +0,0 @@
let a int = 2;
a++;
a *= 3.6 + 1.1;