--no-stdlib

This commit is contained in:
2026-04-11 23:59:15 +02:00
parent 3fd4fcd062
commit 52443c837b
7 changed files with 48 additions and 43 deletions

View File

@@ -45,5 +45,5 @@ func main[] : i64
## Quickstart ## Quickstart
``` ```
cargo install --git https://github.com/antpiasecki/zern cargo install --git https://github.com/antpiasecki/zern
zern -m -r hello.zr zern -r hello.zr
``` ```

View File

@@ -97,7 +97,7 @@ impl<'a> CodegenX86_64<'a> {
format!("section .data\n{}{}", self.data_section, self.output) format!("section .data\n{}{}", self.data_section, self.output)
} }
pub fn emit_prologue(&mut self, use_gcc: bool) -> Result<(), ZernError> { pub fn emit_prologue(&mut self, use_crt: bool) -> Result<(), ZernError> {
emit!( emit!(
&mut self.output, &mut self.output,
"section .note.GNU-stack "section .note.GNU-stack
@@ -154,7 +154,7 @@ _builtin_syscall:
" "
); );
if !use_gcc { if !use_crt {
emit!( emit!(
&mut self.output, &mut self.output,
" "
@@ -220,7 +220,7 @@ _builtin_environ:
Some(t) => t.lexeme.clone(), Some(t) => t.lexeme.clone(),
None => match &initializer { None => match &initializer {
Expr::Literal(token) => { Expr::Literal(token) => {
if token.token_type == TokenType::Number { if token.token_type == TokenType::IntLiteral {
"i64".into() "i64".into()
} else { } else {
return error!(&name.loc, "unable to infer variable type"); return error!(&name.loc, "unable to infer variable type");
@@ -504,17 +504,17 @@ _builtin_environ:
} }
Expr::Grouping(expr) => self.compile_expr(env, expr)?, Expr::Grouping(expr) => self.compile_expr(env, expr)?,
Expr::Literal(token) => match token.token_type { Expr::Literal(token) => match token.token_type {
TokenType::Number => { TokenType::IntLiteral => {
emit!(&mut self.output, " mov rax, {}", token.lexeme); emit!(&mut self.output, " mov rax, {}", token.lexeme);
} }
TokenType::Char => { TokenType::CharLiteral => {
emit!( emit!(
&mut self.output, &mut self.output,
" mov rax, {}", " mov rax, {}",
token.lexeme.chars().nth(1).unwrap() as u8 token.lexeme.chars().nth(1).unwrap() as u8
); );
} }
TokenType::String => { TokenType::StringLiteral => {
// TODO: actual string parsing in the tokenizer // TODO: actual string parsing in the tokenizer
let value = &token.lexeme[1..token.lexeme.len() - 1]; let value = &token.lexeme[1..token.lexeme.len() - 1];

View File

@@ -34,9 +34,11 @@ fn compile_file(args: Args) -> Result<(), ZernError> {
let mut statements = Vec::new(); let mut statements = Vec::new();
parse_std_file!(statements, "std/syscalls.zr"); if args.include_stdlib {
parse_std_file!(statements, "std/std.zr"); parse_std_file!(statements, "std/syscalls.zr");
parse_std_file!(statements, "std/net.zr"); parse_std_file!(statements, "std/std.zr");
parse_std_file!(statements, "std/net.zr");
}
let tokenizer = tokenizer::Tokenizer::new(filename.to_owned(), source); let tokenizer = tokenizer::Tokenizer::new(filename.to_owned(), source);
let parser = parser::Parser::new(tokenizer.tokenize()?); let parser = parser::Parser::new(tokenizer.tokenize()?);
@@ -53,21 +55,21 @@ fn compile_file(args: Args) -> Result<(), ZernError> {
} }
let mut codegen = codegen_x86_64::CodegenX86_64::new(&symbol_table); let mut codegen = codegen_x86_64::CodegenX86_64::new(&symbol_table);
codegen.emit_prologue(args.use_gcc)?; codegen.emit_prologue(args.use_crt)?;
for stmt in statements { for stmt in statements {
codegen.compile_stmt(&mut codegen_x86_64::Env::new(), &stmt)?; codegen.compile_stmt(&mut codegen_x86_64::Env::new(), &stmt)?;
} }
if !args.output_asm { if !args.emit_only {
let out = args.out.unwrap_or_else(|| "out".into()); let out = args.out.unwrap_or_else(|| "out".into());
fs::write(format!("{}.s", out), codegen.get_output()).unwrap(); fs::write(format!("{}.s", out), codegen.get_output()).unwrap();
run_command(format!("nasm -f elf64 -o {}.o {}.s", out, out)); run_command(format!("nasm -f elf64 -o {}.o {}.s", out, out));
if args.use_gcc { if args.use_crt {
run_command(format!( run_command(format!(
"gcc -no-pie -o {} {}.o -flto -Wl,--gc-sections {}", "cc -no-pie -o {} {}.o -flto -Wl,--gc-sections {}",
out, out, args.cflags out, out, args.cflags
)); ));
} else { } else {
@@ -110,9 +112,10 @@ fn run_command(cmd: String) {
struct Args { struct Args {
path: String, path: String,
out: Option<String>, out: Option<String>,
output_asm: bool, emit_only: bool,
include_stdlib: bool,
run_exe: bool, run_exe: bool,
use_gcc: bool, use_crt: bool,
cflags: String, cflags: String,
} }
@@ -121,9 +124,10 @@ impl Args {
let mut out = Args { let mut out = Args {
path: String::new(), path: String::new(),
out: None, out: None,
output_asm: false, emit_only: false,
include_stdlib: true,
run_exe: false, run_exe: false,
use_gcc: false, use_crt: false,
cflags: String::new(), cflags: String::new(),
}; };
@@ -132,26 +136,30 @@ impl Args {
match args.next() { match args.next() {
Some(s) => out.out = Some(s), Some(s) => out.out = Some(s),
None => { None => {
eprintln!("\x1b[91mERROR\x1b[0m: -o option requires a name"); eprintln!("\x1b[91mERROR\x1b[0m: -o option requires a path");
process::exit(1); process::exit(1);
} }
} }
} else if arg == "-S" { } else if arg == "--emit-only" {
out.output_asm = true; out.emit_only = true;
} else if arg == "--no-stdlib" {
out.include_stdlib = false;
} else if arg == "-r" { } else if arg == "-r" {
out.run_exe = true; out.run_exe = true;
} else if arg == "-m" { } else if arg == "-m" {
out.use_gcc = true; out.use_crt = true;
} else if arg == "-C" { } else if arg == "-C" {
match args.next() { match args.next() {
Some(s) => out.cflags = s, Some(s) => out.cflags = s,
None => { None => {
eprintln!("\x1b[91mERROR\x1b[0m: -C option requires a name"); eprintln!("\x1b[91mERROR\x1b[0m: -C option requires a value");
process::exit(1); process::exit(1);
} }
} }
} else if arg == "-h" || arg == "--help" { } else if arg == "-h" || arg == "--help" {
println!("Usage: zern [-o path] [-S] [-r] [-m] [-C cflags] path"); println!(
"Usage: zern [-o path] [-r] [-m] [-C cflags] [--emit-only] [--no-stdlib] path"
);
process::exit(0); process::exit(0);
} else if arg.starts_with('-') { } else if arg.starts_with('-') {
eprintln!("\x1b[91mERROR\x1b[0m: unrecognized option: {}", arg); eprintln!("\x1b[91mERROR\x1b[0m: unrecognized option: {}", arg);
@@ -169,9 +177,8 @@ impl Args {
process::exit(1); process::exit(1);
} }
if !out.use_gcc && !out.cflags.is_empty() { if !out.use_crt && !out.cflags.is_empty() {
// no "ERROR:" since its not an error eprintln!("You can't set CFLAGS if you're not using the C runtime. Add the -m flag.");
eprintln!("You can't set CFLAGS if you're not using gcc. Add the -m flag.");
process::exit(1); process::exit(1);
} }

View File

@@ -255,7 +255,7 @@ impl Parser {
fn const_declaration(&mut self) -> Result<Stmt, ZernError> { fn const_declaration(&mut self) -> Result<Stmt, ZernError> {
let name = self.consume(TokenType::Identifier, "expected const name")?; let name = self.consume(TokenType::Identifier, "expected const name")?;
self.consume(TokenType::Equal, "expected '=' after const name")?; self.consume(TokenType::Equal, "expected '=' after const name")?;
let value = self.consume(TokenType::Number, "expected a number after '='")?; let value = self.consume(TokenType::IntLiteral, "expected a number after '='")?;
Ok(Stmt::Const { name, value }) Ok(Stmt::Const { name, value })
} }
@@ -576,9 +576,9 @@ impl Parser {
fn primary(&mut self) -> Result<Expr, ZernError> { fn primary(&mut self) -> Result<Expr, ZernError> {
if self.match_token(&[ if self.match_token(&[
TokenType::Number, TokenType::IntLiteral,
TokenType::Char, TokenType::CharLiteral,
TokenType::String, TokenType::StringLiteral,
TokenType::True, TokenType::True,
TokenType::False, TokenType::False,
]) { ]) {

View File

@@ -34,9 +34,9 @@ pub enum TokenType {
LessEqual, LessEqual,
Identifier, Identifier,
String, StringLiteral,
Char, CharLiteral,
Number, IntLiteral,
True, True,
False, False,
@@ -240,7 +240,7 @@ impl Tokenizer {
if !self.match_char('\'') { if !self.match_char('\'') {
return error!(self.loc, "expected ' after char literal"); return error!(self.loc, "expected ' after char literal");
} }
self.add_token(TokenType::Char)? self.add_token(TokenType::CharLiteral)?
} }
'"' => { '"' => {
let start_loc = self.loc.clone(); let start_loc = self.loc.clone();
@@ -265,7 +265,7 @@ impl Tokenizer {
} }
self.advance(); self.advance();
self.add_token(TokenType::String)? self.add_token(TokenType::StringLiteral)?
} }
' ' | '\r' => {} ' ' | '\r' => {}
'\n' => { '\n' => {
@@ -343,7 +343,7 @@ impl Tokenizer {
} }
} }
self.add_token(TokenType::Number) self.add_token(TokenType::IntLiteral)
} }
fn scan_identifier(&mut self) -> Result<(), ZernError> { fn scan_identifier(&mut self) -> Result<(), ZernError> {
@@ -392,7 +392,7 @@ impl Tokenizer {
fn add_token(&mut self, token_type: TokenType) -> Result<(), ZernError> { fn add_token(&mut self, token_type: TokenType) -> Result<(), ZernError> {
let mut lexeme: String = self.source[self.start..self.current].iter().collect(); let mut lexeme: String = self.source[self.start..self.current].iter().collect();
if token_type == TokenType::Char || token_type == TokenType::String { if token_type == TokenType::CharLiteral || token_type == TokenType::StringLiteral {
lexeme = self.unescape(&lexeme)?; lexeme = self.unescape(&lexeme)?;
} }

View File

@@ -321,9 +321,9 @@ impl<'a> TypeChecker<'a> {
} }
Expr::Grouping(expr) => self.typecheck_expr(env, expr), Expr::Grouping(expr) => self.typecheck_expr(env, expr),
Expr::Literal(token) => match token.token_type { Expr::Literal(token) => match token.token_type {
TokenType::Number => Ok("i64".into()), TokenType::IntLiteral => Ok("i64".into()),
TokenType::Char => Ok("u8".into()), TokenType::CharLiteral => Ok("u8".into()),
TokenType::String => Ok("str".into()), TokenType::StringLiteral => Ok("str".into()),
TokenType::True => Ok("bool".into()), TokenType::True => Ok("bool".into()),
TokenType::False => Ok("bool".into()), TokenType::False => Ok("bool".into()),
_ => unreachable!(), _ => unreachable!(),

View File

@@ -28,8 +28,6 @@ func run_test[x: str] : void
let run_cmd: str = "./out" let run_cmd: str = "./out"
if str.equal(x, "examples/curl.zr") if str.equal(x, "examples/curl.zr")
run_cmd = str.concat(run_cmd, " http://example.com") run_cmd = str.concat(run_cmd, " http://example.com")
else if str.equal(x, "examples/tokenizer.zr")
run_cmd = str.concat(run_cmd, " test.zr")
let run_start_time: i64 = os.time() let run_start_time: i64 = os.time()
if must(os.run_shell_command?(run_cmd)) != 0 if must(os.run_shell_command?(run_cmd)) != 0