include "statement"
This commit is contained in:
@@ -8,7 +8,7 @@ A very cool language
|
||||
* No libc required!
|
||||
* Produces tiny static executables (11KB for `hello.zr`)
|
||||
* Has type inference, [UFCS](https://en.wikipedia.org/wiki/Uniform_function_call_syntax), variadics, dynamic arrays, hashmaps, DNS resolver, etc.
|
||||
* Growing [standard library](https://git.ton1.dev/toni/zern/src/branch/main/src/std)
|
||||
* Growing [standard library](https://git.ton1.dev/toni/zern/src/branch/main/std)
|
||||
|
||||
## Syntax
|
||||
```rust
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
include "std/net.zr"
|
||||
|
||||
func main[argc: i64, argv: ptr] : i64
|
||||
if argc < 2
|
||||
io.println("ERROR: url is missing")
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
include "std/net.zr"
|
||||
|
||||
func main[] : i64
|
||||
~s, ok := net.listen("127.0.0.1", 8000)
|
||||
if !ok
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
include "std/net.zr"
|
||||
|
||||
func main[] : i64
|
||||
~s, ok := net.create_udp_server("127.0.0.1", 8000)
|
||||
if !ok
|
||||
|
||||
30
src/main.rs
30
src/main.rs
@@ -12,15 +12,6 @@ use std::{
|
||||
|
||||
use tokenizer::ZernError;
|
||||
|
||||
macro_rules! parse_std_file {
|
||||
($statements:expr, $filename:expr) => {
|
||||
let source: String = include_str!($filename).into();
|
||||
let tokenizer = tokenizer::Tokenizer::new($filename.to_owned(), source);
|
||||
let parser = parser::Parser::new(tokenizer.tokenize()?);
|
||||
$statements.extend(parser.parse()?);
|
||||
};
|
||||
}
|
||||
|
||||
fn compile_file(args: Args) -> Result<(), ZernError> {
|
||||
let source = match fs::read_to_string(&args.path) {
|
||||
Ok(x) => x,
|
||||
@@ -32,17 +23,10 @@ fn compile_file(args: Args) -> Result<(), ZernError> {
|
||||
|
||||
let filename = Path::new(&args.path).file_name().unwrap().to_str().unwrap();
|
||||
|
||||
let mut statements = Vec::new();
|
||||
|
||||
if args.include_stdlib {
|
||||
parse_std_file!(statements, "std/std.zr");
|
||||
parse_std_file!(statements, "std/net.zr");
|
||||
parse_std_file!(statements, "std/linux_constants.zr");
|
||||
}
|
||||
|
||||
let tokenizer = tokenizer::Tokenizer::new(filename.to_owned(), source);
|
||||
let mut tokenizer = tokenizer::Tokenizer::new(filename.to_owned(), source);
|
||||
tokenizer.include_file("std/std.zr".into())?;
|
||||
let parser = parser::Parser::new(tokenizer.tokenize()?);
|
||||
statements.extend(parser.parse()?);
|
||||
let statements = parser.parse()?;
|
||||
|
||||
let mut symbol_table = symbol_table::SymbolTable::new();
|
||||
for stmt in &statements {
|
||||
@@ -114,7 +98,6 @@ struct Args {
|
||||
out: Option<String>,
|
||||
emit_only: bool,
|
||||
emit_debug: bool,
|
||||
include_stdlib: bool,
|
||||
run_exe: bool,
|
||||
use_crt: bool,
|
||||
cflags: String,
|
||||
@@ -129,7 +112,6 @@ impl Args {
|
||||
out: None,
|
||||
emit_only: false,
|
||||
emit_debug: false,
|
||||
include_stdlib: true,
|
||||
run_exe: false,
|
||||
use_crt: false,
|
||||
cflags: String::new(),
|
||||
@@ -146,8 +128,6 @@ impl Args {
|
||||
}
|
||||
} else if arg == "--emit-only" {
|
||||
out.emit_only = true;
|
||||
} else if arg == "--no-stdlib" {
|
||||
out.include_stdlib = false;
|
||||
} else if arg == "-r" {
|
||||
out.run_exe = true;
|
||||
} else if arg == "-m" {
|
||||
@@ -163,9 +143,7 @@ impl Args {
|
||||
}
|
||||
}
|
||||
} else if arg == "-h" || arg == "--help" {
|
||||
println!(
|
||||
"Usage: zern [-o path] [-r] [-m] [-g] [-C cflags] [--emit-only] [--no-stdlib] path"
|
||||
);
|
||||
println!("Usage: zern [-o path] [-r] [-m] [-g] [-C cflags] [--emit-only] path");
|
||||
process::exit(0);
|
||||
} else if arg.starts_with('-') {
|
||||
eprintln!("\x1b[91mERROR\x1b[0m: unrecognized option: {arg}");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{cmp::Ordering, fmt};
|
||||
use std::{cmp::Ordering, fmt, fs, path::Path};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TokenType {
|
||||
@@ -378,6 +378,11 @@ impl Tokenizer {
|
||||
}
|
||||
|
||||
let lexeme: String = self.source[self.start..self.current].iter().collect();
|
||||
|
||||
if lexeme == "include" {
|
||||
return self.scan_include();
|
||||
}
|
||||
|
||||
self.add_token(match lexeme.as_str() {
|
||||
"const" => TokenType::KeywordConst,
|
||||
"if" => TokenType::KeywordIf,
|
||||
@@ -401,6 +406,49 @@ impl Tokenizer {
|
||||
})
|
||||
}
|
||||
|
||||
fn scan_include(&mut self) -> Result<(), ZernError> {
|
||||
if !self.match_char(' ') {
|
||||
return error!(self.loc, "expected a space after 'include'");
|
||||
}
|
||||
|
||||
if self.peek() != '"' {
|
||||
return error!(self.loc, "expected '#' after 'include '");
|
||||
}
|
||||
self.advance();
|
||||
|
||||
let path_start = self.current;
|
||||
while !self.eof() && self.peek() != '"' {
|
||||
self.advance();
|
||||
}
|
||||
|
||||
if self.eof() {
|
||||
return error!(self.loc, "unterminated string after 'include'");
|
||||
}
|
||||
|
||||
let path: String = self.source[path_start..self.current].iter().collect();
|
||||
self.advance(); // consume closing quote
|
||||
|
||||
self.include_file(path)
|
||||
}
|
||||
|
||||
// TODO: circular includes lead to "fatal runtime error: stack overflow, aborting"
|
||||
pub fn include_file(&mut self, path: String) -> Result<(), ZernError> {
|
||||
let source = match fs::read_to_string(&path) {
|
||||
Ok(x) => x,
|
||||
Err(_) => {
|
||||
return error!(self.loc, format!("failed to include {}", path));
|
||||
}
|
||||
};
|
||||
|
||||
let filename = Path::new(&path).file_name().unwrap().to_str().unwrap();
|
||||
|
||||
let tokenizer = Tokenizer::new(filename.to_owned(), source);
|
||||
self.tokens.extend(tokenizer.tokenize()?);
|
||||
self.tokens.pop(); // remove inner Eof
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn match_char(&mut self, expected: char) -> bool {
|
||||
if self.eof() || self.peek() != expected {
|
||||
false
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
include "std/linux_constants.zr"
|
||||
|
||||
func panic[msg: str] : void
|
||||
io.print("PANIC: ")
|
||||
io.println(msg)
|
||||
Reference in New Issue
Block a user