4 releases (stable)
1.0.2 | Mar 20, 2023 |
---|---|
1.0.1 | Mar 19, 2023 |
0.1.0 | Jan 28, 2023 |
#1068 in Parser implementations
75 downloads per month
330KB
8K
SLoC
phoron_asm
This project defines Phoron
, a Jasmin-compatible assembler for the JVM Instruction Set..
For the specification, please refer to the specification document.
For the testable grammar for Phoron
, please refer to the grammar.
For the design, please refer to the Design doc.
Build
$ cargo build --release
Sample Run
For the sample source file HelloWorld.pho:
.class public HelloWorld
.super java/lang/Object
.method public <init>()V
aload_0
invokespecial java/lang/Object/<init>()V ; super ()
return
.end method
.method public static main([Ljava/lang/String;)V
.limit stack 2
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "Hello, world"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
return
.end method
We generate the class
file:
$ cargo run --release -- -f samples/HelloWorld.pho
Sanity-check to ensure that the generated class
file is valid:
$ javap -v HelloWorld.class
Classfile /Users/z0ltan/dev/oyi-lang/phoron_asm/samples/HelloWorld.class
Last modified 19-Mar-2023; size 389 bytes
SHA-256 checksum 533a66c051831cba84a32b20d38c4bb20d68b78aabc137d7c7fb3cc864ff8bf9
Compiled from "./samples/HelloWorld.pho"
public class HelloWorld
minor version: 3
major version: 45
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #4 // HelloWorld
super_class: #6 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Utf8 SourceFile
#2 = Utf8 ./samples/HelloWorld.pho
#3 = Utf8 HelloWorld
#4 = Class #3 // HelloWorld
#5 = Utf8 java/lang/Object
#6 = Class #5 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = NameAndType #7:#8 // "<init>":()V
#11 = Methodref #6.#10 // java/lang/Object."<init>":()V
#12 = Utf8 main
#13 = Utf8 ([Ljava/lang/String;)V
#14 = Utf8 java/lang/System
#15 = Class #14 // java/lang/System
#16 = Utf8 out
#17 = Utf8 Ljava/io/PrintStream;
#18 = NameAndType #16:#17 // out:Ljava/io/PrintStream;
#19 = Fieldref #15.#18 // java/lang/System.out:Ljava/io/PrintStream;
#20 = Utf8 Hello, world
#21 = String #20 // Hello, world
#22 = Utf8 java/io/PrintStream
#23 = Class #22 // java/io/PrintStream
#24 = Utf8 println
#25 = Utf8 (Ljava/lang/String;)V
#26 = NameAndType #24:#25 // println:(Ljava/lang/String;)V
#27 = Methodref #23.#26 // java/io/PrintStream.println:(Ljava/lang/String;)V
{
public HelloWorld();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #11 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #19 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #21 // String Hello, world
5: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
SourceFile: "./samples/HelloWorld.pho"
and then we can test it out by running the class
file:
$ java -cp . HelloWorld
Hello, world
Sample API usage
The same example, but using the API instead.
use std::{
error::Error,
fmt, fs,
io::BufWriter,
path::{Path, PathBuf},
};
use phoron_asm::{
codegen::Codegen, cp_analyzer::ConstantPoolAnalyzer, lexer::Lexer, parser::Parser,
sourcefile::SourceFile,
};
#[derive(Debug)]
pub struct RunError {
message: String,
}
impl Error for RunError {}
impl fmt::Display for RunError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
fn process_file(src_file: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
let outfile = src_file.with_extension("class");
let source_file = SourceFile::new(src_file).map_err(|err| RunError {
message: err.to_string(),
})?;
let mut parser = Parser::new(Lexer::new(&source_file));
let ast = parser.parse().unwrap();
if parser.errored() {
return Err(Box::new(RunError {
message: "errors encountered during parsing and typ-checking".into(),
}));
}
let mut cp_analyzer = ConstantPoolAnalyzer::new();
let cp = cp_analyzer.analyze(&ast).map_err(|err| RunError {
message: err.to_string(),
})?;
let mut outfile_w = BufWriter::new(fs::File::create(&outfile)?);
let mut codegen = Codegen::new(&mut outfile_w);
codegen.gen_bytecode(&ast, &cp).map_err(|err| RunError {
message: err.to_string(),
})?;
Ok(())
}
fn main() {
let src_file = Path::new("./samples/").join("HelloWorld.pho");
match process_file(&src_file) {
Err(err) => eprintln!("{err}"),
Ok(_) => println!("Class file generated"),
}
}
Running it:
$ cargo run --release
Class file generated
$ javap -v samples/HelloWorld.class
Classfile /Users/z0ltan/dev/playground/phoron_asm_demo/samples/HelloWorld.class
Last modified 19-Mar-2023; size 389 bytes
SHA-256 checksum 533a66c051831cba84a32b20d38c4bb20d68b78aabc137d7c7fb3cc864ff8bf9
Compiled from "./samples/HelloWorld.pho"
public class HelloWorld
minor version: 3
major version: 45
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #4 // HelloWorld
super_class: #6 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Utf8 SourceFile
#2 = Utf8 ./samples/HelloWorld.pho
#3 = Utf8 HelloWorld
#4 = Class #3 // HelloWorld
#5 = Utf8 java/lang/Object
#6 = Class #5 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = NameAndType #7:#8 // "<init>":()V
#11 = Methodref #6.#10 // java/lang/Object."<init>":()V
#12 = Utf8 main
#13 = Utf8 ([Ljava/lang/String;)V
#14 = Utf8 java/lang/System
#15 = Class #14 // java/lang/System
#16 = Utf8 out
#17 = Utf8 Ljava/io/PrintStream;
#18 = NameAndType #16:#17 // out:Ljava/io/PrintStream;
#19 = Fieldref #15.#18 // java/lang/System.out:Ljava/io/PrintStream;
#20 = Utf8 Hello, world
#21 = String #20 // Hello, world
#22 = Utf8 java/io/PrintStream
#23 = Class #22 // java/io/PrintStream
#24 = Utf8 println
#25 = Utf8 (Ljava/lang/String;)V
#26 = NameAndType #24:#25 // println:(Ljava/lang/String;)V
#27 = Methodref #23.#26 // java/io/PrintStream.println:(Ljava/lang/String;)V
{
public HelloWorld();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #11 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #19 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #21 // String Hello, world
5: invokevirtual #27 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
SourceFile: "./samples/HelloWorld.pho"
$ java -cp "./samples" HelloWorld
Hello, world
Dependencies
~150KB