dev/pbs #8

Merged
bquarkz merged 74 commits from dev/pbs into master 2026-02-03 15:28:31 +00:00
2 changed files with 93 additions and 281 deletions
Showing only changes of commit b814a6c48d - Show all commits

View File

@ -0,0 +1,93 @@
use prometeu_compiler::compiler;
use prometeu_bytecode::pbc::parse_pbc;
use prometeu_bytecode::disasm::disasm;
use std::fs;
use tempfile::tempdir;
#[test]
fn test_golden_bytecode_snapshot() {
let dir = tempdir().unwrap();
let project_dir = dir.path();
fs::write(
project_dir.join("prometeu.json"),
r#"{
"script_fe": "pbs",
"entry": "main.pbs"
}"#,
).unwrap();
let code = r#"
declare contract Gfx host {}
fn helper(val: int) -> int {
return val * 2;
}
fn main() {
Gfx.clear(0);
let x = 10;
if (x > 5) {
let y = helper(x);
}
let buf = alloc int;
mutate buf as b {
let current = b + 1;
}
}
"#;
fs::write(project_dir.join("main.pbs"), code).unwrap();
let unit = compiler::compile(project_dir).expect("Failed to compile");
let pbc = parse_pbc(&unit.rom).expect("Failed to parse PBC");
let instrs = disasm(&pbc.rom).expect("Failed to disassemble");
let mut disasm_text = String::new();
for instr in instrs {
let operands_str = instr.operands.iter()
.map(|o| format!("{:?}", o))
.collect::<Vec<_>>()
.join(" ");
let line = if operands_str.is_empty() {
format!("{:04X} {:?}\n", instr.pc, instr.opcode)
} else {
format!("{:04X} {:?} {}\n", instr.pc, instr.opcode, operands_str.trim())
};
disasm_text.push_str(&line);
}
let expected_disasm = r#"0000 GetLocal U32(0)
0006 PushConst U32(1)
000C Mul
000E Ret
0010 PushConst U32(2)
0016 Syscall U32(4097)
001C PushConst U32(3)
0022 SetLocal U32(0)
0028 GetLocal U32(0)
002E PushConst U32(4)
0034 Gt
0036 JmpIfFalse U32(94)
003C Jmp U32(66)
0042 GetLocal U32(0)
0048 Call U32(0) U32(1)
0052 SetLocal U32(1)
0058 Jmp U32(100)
005E Jmp U32(100)
0064 Alloc
0066 SetLocal U32(1)
006C GetLocal U32(1)
0072 LoadRef U32(0)
0078 SetLocal U32(2)
007E GetLocal U32(2)
0084 PushConst U32(5)
008A Add
008C SetLocal U32(3)
0092 GetLocal U32(2)
0098 StoreRef U32(0)
009E Ret
"#;
assert_eq!(disasm_text, expected_disasm);
}

View File

@ -1,281 +0,0 @@
# arquivo: g/base.pbs
# services => singletons que soh possuem metodos, usados para DI
// default: vis?vel s? no arquivo
// mod X: exporta para o m?dulo (diret?rio)
// pub X: exporta para quem importar o arquivo (API p?blica do arquivo)
// quem importa o arquivo nao pode usar o mod, arquivos no mesmo diretorio nao precisam de import
pub service base
{
// fn define um funcao
fn do_something(a: long, b: int, c: float, d: char, e: string, f: bool): void
{
// do something
}
}
# arquivo: a/service.pbs
import { base } from "@g/base.pbs";
// service sem pub (default) => private, soh sera acessivel dentro do arquivo atual
service bla
{
fn do_something_else(): void
{
// do something else
}
}
// mod indica que esse sera exportado para o modulo (cada diretorio eh um modulo) - no caso "@a"
mod service bla2
{
fn do_something_else_2(): void
{
// do something else 2
}
}
pub service obladi
{
fn do_something(a: long, b: int, c: float, d: char, e: string, f: bool): void
{
base.do_something(a,b,c,d,e,f);
bla.do_something_else();
}
}
# arquivo: b/service.pbs
import { base } from "@g/base.pbs";
pub service oblada
{
fn do_something(a: long, b: int, c: float, d: char, e: string, f: bool): void
{
base.do_something(a,b,c,d,e,f);
}
}
#arquivo: main.pbs (root)
# import carrega aquela API: @ sempre se referencia a raiz do projeto
import { obladi as sa } from "@a/service.pbs";
import { oblada as sb } from "@b/service.pbs";
// funcoes podem ser declaradas fora de services, mas serao SEMPRE private
fn some(a: int, b: int): int // recebe a e b e retorna a soma
{
return a + b;
}
fn frame(): void
{
sa.do_something(1l,2,3.33,'4',"5",true); // chama o metodo do service de a
sb.do_something(1l,2,3.33,'4',"5",true); // chama o metodo do service de b
// tipos
// void: nao retorna nada
// int : i32
// long: i64
// float: f32
// double: f64
// char: u32 nao sei se sera muito efetivo, me lembro de C (char = unsigned int, UTF-8) precisa de 32 aqui?
// string: handle imut?vel para constant pool (e futuramente heap)
// bool: true/false (1 bit)
// nao eh possivel ter duas variaveis com o mesmo nome, isso eh soh um exemplo
// coercao implicita:
Sugest?o simples e consistente (recomendo para v0):
* Widen num?rico impl?cito permitido:
int -> long -> float -> double (se voc? quiser float->double tamb?m)
* Narrow/truncar NUNCA impl?cito (sempre cast expl?cito)
ent?o long = 1.5 exige as long
int = 1.5 exige as int
use as como cast
// comentario de linha
/* comentario de bloco */
let x: int = 1; // tipo explicito
let y = 1; // tipo implicito, com inferencia direta para tipos primitivos
// z nao existe aqui!
{ // scope
let z: int = 1;
}
// z nao existe aqui!
let resultado = soma(1,2); // chama a fn soma e associa a uma variavel soma - sem problemas
if (resultado > 10)
{
// sys.println(resultado);
}
else if (resultado > 100)
{
// sys.println(resultado);
}
else
{
// sys.println(resultado);
}
for i from [0..10] // conta de 0 a 10 i default sempre int
{
}
// porem tb eh possivel
for i: long from [0L..10L]
{
}
for i from [0..10[ // conta de 0 a 9
{
}
for i from ]0..10] // conta de 1 a 10
{
}
for i from ]0..10[ // conta de 1 a 9
{
}
for i from [10..0] // conta de 10 a 0
{
}
}
// definicao de uma struct, x e y sao privados por default
define Vector(x: float, y: float)
[
(): (0, 0)
(a: float): (a, a)
{
normalize();
}
]
[[ // bloco estatico (opcional)
// o bloco estatico deve ser usado para definir constantes desse mesmo tipo e nao outro, por isso declaracao
// atraves de construtores
// assim podemos ter um tipo de enum com valores estaticos/constantes sem precisar de uma classe/instancia (vao para o constant pool)
ZERO: ()
ONE: (1, 1)
]]
{
// permitir x como sugar para this.x apenas dentro de m?todos de struct e apenas se n?o houver vari?vel local com mesmo nome. Caso exista, exige this.x.
// this s? ? permitido como tipo dentro do corpo de um define.
// this resolve para o tipo do define atual (Vector, Model, etc.)
// this tamb?m ? permitido como valor (this.x) dentro de m?todos.
// fora do define, this ? erro.
pub fn add(x: float, y: float): this
{
this.x += x;
this.y += y;
}
// privado nao pode ser usado fora da struct
fn normalize(): void
{
let l = sqrt(x*x + y*y);
x /= l;
y /= l;
}
// literals sao sempre stack allocated
// nesse caso aqui, como Vector nao eh alterado, ou seja, o valor de x e y nao muda
// o compilador passa a ref de v
// acesso aos campos
// private ? por tipo, n?o por inst?ncia
// Ent?o Vector pode acessar Vector.x em qualquer Vector.
pub fn dot(v: Vector): float
{
return x*v.x + y*v.y;
}
}
define Model(c: Vector)
[
(): (Vector())
]
{
// nesse caso, por exemplo, como v vai ser alterado, Vector deve ser passado como mutavel (seus valores sao copiados)
// e o Vector de origem fica inalterado
fn sum(v: mut Vector): Vector
{
return v.add(c.x, c.y);
}
}
# arquivo: z/contract.pbs
// SDK ... o compilador injeta as defs de gfx aqui
contract gfx // nome do contrato eh gfx
{
fn drawText(x: int, y: int, text: string, color: Color): void;
}
contract interface // nome do contrato eh interface (eh mongol mas nao sou muito criativo)
{
fn bla(x: int, y: int): void;
}
pub service bla1: interface
{
fn bla(x: int, y: int): void
{
// do something
}
}
pub service bla2: interface
{
fn bla(x: int, y: int): void
{
// do something else
}
}
>>
Regra final recomendada (simples e limpa)
Existem dois namespaces globais
Tipos: define, interface, contract
Valores: fn, let, service
Regras
Dentro de um mesmo escopo:
? dois s?mbolos de valor com mesmo nome ? erro
? um valor n?o pode ter o mesmo nome de um tipo vis?vel ? erro (opcional, mas recomendado)
Shadowing entre escopos:
* permitido apenas para vari?veis
* n?o permitido para fun??es/services (para evitar confus?o)
>>
8) return v.add(c.x, c.y); com v: mut Vector
Se mut significa ?c?pia mut?vel?, ent?o add modifica v e retorna this (o mesmo v). Ok.
Mas a?:
* add precisa declarar retorno this e o compiler deve entender que this = Vector.
* this s? existe no contexto de define.
declare Struct // struct sem valores eh um service :D
{
pub fn sum(v): Struct
{
return this;
}
// Isso eh soh sugar para o mesmo acima
pub fn sum(v): this
{
}
// OU
pub fn sum(v): me // mais uma keyword...
{
}
}
// para condicionais :
let x = when a == b then 1 else 2;