summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--grammar105
-rw-r--r--oryxc/src/arena.rs73
-rw-r--r--oryxc/src/compiler.rs190
-rw-r--r--oryxc/src/hashtrie.rs219
-rw-r--r--oryxc/src/lexer.rs9
-rw-r--r--oryxc/src/main.rs7
-rw-r--r--oryxc/src/parser.rs28
-rw-r--r--oryxc/src/prelude.rs36
-rw-r--r--test.yx (renamed from test.xy)2
9 files changed, 555 insertions, 114 deletions
diff --git a/grammar b/grammar
new file mode 100644
index 0000000..2042e97
--- /dev/null
+++ b/grammar
@@ -0,0 +1,105 @@
+root: top-level*;
+
+top-level: def-bind ";";
+
+def-bind: "def" decl-list "=" expr-list;
+
+decl-list: (ident expr? ",")* ident expr?;
+expr-list: (expr ",")* expr;
+
+expr
+ : ident
+ | number
+ | string
+ | function-proto
+ | function
+ | function-call
+ | expr "^" /* Pointer Dereference */
+ | unary-op expr
+ | expr binary-op expr
+ | "(" expr ")"
+ ;
+
+function: function-proto block-statement;
+function-call: expr "(" expr-list? ")";
+function-proto: "func" "(" decl-list? ")" (expr | "(" expr-list ")")?;
+
+block-statement: "{" statement* "}";
+statement
+ : block-statement
+ | (expr
+ | def-bind
+ | expr-list "=" expr-list
+ | "return" expr-list?
+ )?
+ ";"
+ ;
+
+unary-op
+ : "^" /* Pointer */
+ | "~"
+ | "+"
+ | "-"
+ | "&"
+ ;
+
+binary-op
+ : "+"
+ | "-"
+ | "*"
+ | "/"
+ | "/%" /* Divrem */
+ | "%" /* Remainder */
+ | "%%" /* Modulus */
+ | "~" /* XOR */
+ | "&"
+ | "|"
+ | "&~" /* Bitwise-ANDNOT */
+ | "<<"
+ | "<<<" /* Left Rotate */
+ | ">>"
+ | ">>>" /* Right Rotate */
+ | "&&"
+ | "||"
+ | "=="
+ | "!="
+ | "<="
+ | ">="
+ | "<"
+ | ">"
+ ;
+
+ident: xid-start xid-continue*;
+xid-start
+ : \p{XID_Start}
+ | "_"
+ | "$"
+ ;
+xid-continue
+ : \p{XID_Continue}
+ | "′"
+ | "″"
+ | "‴"
+ | "⁗"
+ ;
+
+string: "\"" string-part+ "\"";
+string-part: [^"] | "\\\"";
+
+number
+ : number-bin
+ | number-oct
+ | number-dec
+ | number-hex
+ ;
+
+number-bin: "#b" number-rest<[01]>;
+number-oct: "#o" number-rest<[01234567>;
+number-dec: "#d"? number-rest<[0123456789]>;
+number-hex: "#x" number-rest<[0123456789ABCDEF]>;
+
+(* <a> is the alphabet *)
+number-rest<a>: (number-word<a> ("." number-word<a>?)?
+ | "." number-word<a>)
+ ("e" [-+] number-word<a>)?;
+number-word<a>: <a> ((<a> | "'")* <a>)?;
diff --git a/oryxc/src/arena.rs b/oryxc/src/arena.rs
index 2268c9b..34a48cb 100644
--- a/oryxc/src/arena.rs
+++ b/oryxc/src/arena.rs
@@ -31,10 +31,10 @@ pub struct GlobalArena {
impl GlobalArena {
pub fn new(blksz: usize) -> Self {
- Self {
+ return Self {
blksz,
blocks: Mutex::new(Vec::new()),
- }
+ };
}
fn allocate_block(&self, layout: Layout) -> RawBlock {
@@ -179,7 +179,7 @@ impl<'a> LocalArena<'a> {
return unsafe { slice::from_raw_parts_mut(ptr, len) };
}
- fn mark(&self) -> Mark {
+ pub fn mark(&self) -> Mark {
return Mark {
blk: self.curblk.get(),
beg: self.beg.get(),
@@ -187,7 +187,7 @@ impl<'a> LocalArena<'a> {
};
}
- fn restore(&self, mark: Mark) {
+ pub fn restore(&self, mark: Mark) {
self.curblk.set(mark.blk);
self.beg.set(mark.beg);
self.end.set(mark.end);
@@ -214,44 +214,47 @@ impl<'s, 'a> ScopedArena<'s, 'a> {
return self.inner.alloc(value);
}
- pub fn alloc_slice<T>(&self, len: usize) -> &'a mut [T] {
+ pub fn alloc_slice<T>(&self, len: usize) -> &'s mut [T] {
return self.inner.alloc_slice(len);
}
}
-#[test]
-fn test_alloc_slice() {
- let arena_global = GlobalArena::new(8);
- let arena_local_1 = LocalArena::new(&arena_global);
- let arena_local_2 = LocalArena::new(&arena_global);
-
- let s1 = arena_local_1.alloc_slice(8);
- let s2 = arena_local_2.alloc_slice(4);
- assert_eq!(s1.len(), 8);
- assert_eq!(s2.len(), 4);
-
- for i in 0..s1.len() {
- s1[i] = i;
- }
- for i in 0..s2.len() {
- s2[i] = i;
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn test_alloc_slice() {
+ let arena_global = GlobalArena::new(8);
+ let arena_local_1 = LocalArena::new(&arena_global);
+ let arena_local_2 = LocalArena::new(&arena_global);
+
+ let s1 = arena_local_1.alloc_slice(8);
+ let s2 = arena_local_2.alloc_slice(4);
+ assert_eq!(s1.len(), 8);
+ assert_eq!(s2.len(), 4);
+
+ for i in 0..s1.len() {
+ s1[i] = i;
+ }
+ for i in 0..s2.len() {
+ s2[i] = i;
+ }
}
-}
-#[test]
-fn test_arena_grows() {
- let arena_global = GlobalArena::new(8);
- let arena_local = LocalArena::new(&arena_global);
+ #[test]
+ fn test_arena_grows() {
+ let arena_global = GlobalArena::new(8);
+ let arena_local = LocalArena::new(&arena_global);
- let s1 = arena_local.alloc_slice(8);
- let s2 = arena_local.alloc_slice(69);
- assert_eq!(s1.len(), 8);
- assert_eq!(s2.len(), 69);
+ let s1 = arena_local.alloc_slice(8);
+ let s2 = arena_local.alloc_slice(69);
+ assert_eq!(s1.len(), 8);
+ assert_eq!(s2.len(), 69);
- for i in 0..s1.len() {
- s1[i] = i;
- }
- for i in 0..s2.len() {
- s2[i] = i;
+ for i in 0..s1.len() {
+ s1[i] = i;
+ }
+ for i in 0..s2.len() {
+ s2[i] = i;
+ }
}
}
diff --git a/oryxc/src/compiler.rs b/oryxc/src/compiler.rs
index 98947f6..3bfe6c1 100644
--- a/oryxc/src/compiler.rs
+++ b/oryxc/src/compiler.rs
@@ -34,10 +34,11 @@ use crate::arena::{
LocalArena,
};
use crate::errors::OryxError;
+use crate::hashtrie::HTrie;
use crate::intern::Interner;
use crate::lexer::Token;
use crate::parser::{
- AstNode,
+ Ast,
AstType,
};
use crate::prelude::*;
@@ -51,12 +52,11 @@ use crate::{
#[allow(dead_code)]
pub struct FileData {
- pub name: OsString,
- pub buffer: String,
- pub tokens: OnceLock<Soa<Token>>,
- pub ast: OnceLock<Soa<AstNode>>,
- pub extra_data: OnceLock<Vec<u32>>,
- pub symtab: DashMap<(ScopeId, SymbolId), Symbol>,
+ pub name: OsString,
+ pub buffer: String,
+ pub tokens: OnceLock<Soa<Token>>,
+ pub ast: OnceLock<Ast>,
+ pub scopes: OnceLock<HTrie<ScopeId, Scope>>,
}
impl FileData {
@@ -72,14 +72,13 @@ impl FileData {
fs::File::open(&name)?.read_to_string(&mut buffer)?;
buffer.push_str(unsafe { std::str::from_utf8_unchecked(&PAD) });
- Ok(Self {
+ return Ok(Self {
name,
buffer,
tokens: OnceLock::new(),
ast: OnceLock::new(),
- extra_data: OnceLock::new(),
- symtab: DashMap::new(),
- })
+ scopes: OnceLock::new(),
+ });
}
}
@@ -93,15 +92,15 @@ pub enum JobType {
file: FileId,
fdata: Arc<FileData>,
},
- FindSymbolsInScope {
- fdata: Arc<FileData>,
- scope: ScopeId,
- block: u32,
+ IndexScopeConstants {
+ fdata: Arc<FileData>,
+ block: u32,
+ parent: ScopeId,
},
ResolveDefBind {
fdata: Arc<FileData>,
- scope: ScopeId,
node: u32,
+ scope: ScopeId,
},
}
@@ -138,17 +137,34 @@ impl<'a> CompilerState<'a> {
}
}
+ /// Generate a new ID for a job, scope, etc.
+ #[inline(always)]
+ fn genid(&self) -> usize {
+ return self.next_id.fetch_add(1, Ordering::Relaxed);
+ }
+
+ /// Build a new job of type KIND.
+ #[inline(always)]
fn job_new(&self, kind: JobType) -> Job {
- let id = self.next_id.fetch_add(1, Ordering::Relaxed);
+ let id = self.genid();
return Job { id, kind };
}
- /// Push a job onto a worker's local queue and wake all threads.
+ /// Push a job onto a worker’s local queue and wake all threads.
+ #[inline(always)]
fn job_push(&self, queue: &Worker<Job>, job: Job) {
self.njobs.fetch_add(1, Ordering::Relaxed);
queue.push(job);
self.wake_all();
}
+
+ /// Signal a job completion by decrementing the job count.
+ ///
+ /// Returns the number of remaining jobs
+ #[inline(always)]
+ fn job_complete(&self) -> usize {
+ return self.njobs.fetch_sub(1, Ordering::Release) - 1;
+ }
}
/// Initialize compiler state and drive all source files through the
@@ -218,20 +234,38 @@ where
let workers = unsafe { workers.assume_init() };
let stealers = Arc::from(unsafe { stealers.assume_init() });
- let mut worker_threads = Box::new_uninit_slice(workers.len());
+ let mut ok = true;
thread::scope(|s| {
+ let mut handles = Box::new_uninit_slice(workers.len());
+ let mut worker_threads = Box::new_uninit_slice(handles.len());
+
for (i, w) in workers.into_iter().enumerate() {
let stealers = Arc::clone(&stealers);
let state = Arc::clone(&state);
let arena = LocalArena::new(&state.global_arena);
let handle = s.spawn(move || worker_loop(i, state, w, stealers));
worker_threads[i].write(handle.thread().clone());
+ handles[i].write(handle);
}
let _ = state
.worker_threads
.set(unsafe { worker_threads.assume_init() });
state.wake_all();
+
+ for h in handles {
+ match unsafe { h.assume_init() }.join() {
+ Ok(thrd_ok) => {
+ if !thrd_ok {
+ ok = false;
+ }
+ },
+ Err(_) => ok = false,
+ }
+ }
});
+ if !ok {
+ process::exit(1);
+ }
}
/// Steal and execute jobs until all work is complete.
@@ -240,7 +274,8 @@ fn worker_loop(
c_state: Arc<CompilerState>,
queue: Worker<Job>,
stealers: Arc<[Stealer<Job>]>,
-) {
+) -> bool {
+ let mut ok = true;
let arena = LocalArena::new(&c_state.global_arena);
loop {
@@ -250,19 +285,21 @@ fn worker_loop(
let n = c_state.njobs.load(Ordering::Acquire);
if n == 0 {
c_state.wake_all();
- return;
+ return ok;
}
thread::park();
continue;
};
- match job.kind {
- JobType::Lex { file, fdata } => {
- let tokens =
- lexer::tokenize(&fdata.buffer).unwrap_or_else(|e| {
+ let result = match job.kind {
+ JobType::Lex { file, fdata } => 'blk: {
+ let tokens = match lexer::tokenize(&fdata.buffer) {
+ Ok(xs) => xs,
+ Err(e) => {
emit_errors(&fdata, iter::once(e));
- process::exit(1)
- });
+ break 'blk false;
+ },
+ };
if c_state.flags.debug_lexer {
let mut handle = io::stderr().lock();
@@ -276,65 +313,66 @@ fn worker_loop(
&queue,
c_state.job_new(JobType::Parse { file, fdata }),
);
+ true
},
- JobType::Parse { file, fdata } => {
- let (ast, extra_data) = parser::parse(
- fdata.tokens.get().unwrap(),
- )
- .unwrap_or_else(|errs| {
- emit_errors(&fdata, errs);
- process::exit(1)
- });
+ JobType::Parse { file, fdata } => 'blk: {
+ let ast = match parser::parse(fdata.tokens.get().unwrap()) {
+ Ok(ast) => ast,
+ Err(errs) => {
+ emit_errors(&fdata, errs);
+ break 'blk false;
+ },
+ };
if c_state.flags.debug_parser {
let mut handle = io::stderr().lock();
- for n in ast.iter() {
+ for n in ast.nodes.iter() {
let _ = write!(handle, "{n:?}\n");
}
}
- let root = (ast.len() - 1) as u32;
+ let root = (ast.nodes.len() - 1) as u32;
fdata.ast.set(ast).unwrap();
- fdata.extra_data.set(extra_data).unwrap();
+ fdata.scopes.set(HTrie::new()).unwrap();
c_state.job_push(
&queue,
- c_state.job_new(JobType::FindSymbolsInScope {
+ c_state.job_new(JobType::IndexScopeConstants {
fdata,
- scope: ScopeId::GLOBAL,
block: root,
+ parent: ScopeId::INVALID,
}),
);
+ true
},
- JobType::FindSymbolsInScope {
+ JobType::IndexScopeConstants {
fdata,
- scope,
block,
+ parent,
} => {
let tokens = fdata.tokens.get().unwrap();
let ast = fdata.ast.get().unwrap();
- let extra_data = fdata.extra_data.get().unwrap();
- let SubNodes(beg, nstmts) = ast.sub()[block as usize];
+ let SubNodes(beg, nstmts) = ast.nodes.sub()[block as usize];
let mut errors = Vec::new();
+ let scope = Scope::new(parent);
+ /* First pass inserts all the symbols in this scope into the
+ * symbol table */
for i in beg..beg + nstmts {
- let multi_def_bind = extra_data[i as usize];
-
- if ast.kind()[multi_def_bind as usize]
- != AstType::MultiDefBind
+ let node = ast.extra[i as usize];
+ if ast.nodes.kind()[node as usize] != AstType::MultiDefBind
{
continue;
}
- let def_idents = ast.sub()[multi_def_bind as usize].0;
- let nidents = extra_data[def_idents as usize];
+ let identsbeg = ast.nodes.sub()[node as usize].0;
+ let nidents = ast.extra[identsbeg as usize];
for j in 0..nidents {
- let ident =
- extra_data[(def_idents + 1 + j * 2) as usize];
+ let ident = ast.extra[(identsbeg + 1 + j * 2) as usize];
let span = tokens.view()[ident as usize];
/* Make string slice lifetime 'static */
@@ -343,14 +381,16 @@ fn worker_loop(
};
let symid = c_state.interner.intern(view);
- let sym = Symbol::default();
+ let sym = Symbol {
+ state: ResolutionState::Unresolved,
+ kind: SymbolType::Constant,
+ };
if let Some(mut sym) =
- fdata.symtab.insert((scope, symid), sym)
+ scope.symtab.insert(symid, sym, &arena)
{
sym.state = ResolutionState::Poisoned;
- fdata.symtab.insert((scope, symid), sym);
-
+ scope.symtab.insert(symid, sym, &arena);
errors.push(OryxError::new(
span,
format!(
@@ -359,21 +399,44 @@ fn worker_loop(
));
}
}
+ }
+
+ let scopeid = if parent == ScopeId::INVALID {
+ ScopeId::GLOBAL
+ } else {
+ ScopeId(c_state.genid())
+ };
+ fdata.scopes.get().unwrap().insert(scopeid, scope, &arena);
+ /* Second pass emits jobs to resolve types */
+ for i in beg..beg + nstmts {
+ let node = ast.extra[i as usize];
+ if ast.nodes.kind()[node as usize] != AstType::MultiDefBind
+ {
+ continue;
+ }
c_state.job_push(
&queue,
c_state.job_new(JobType::ResolveDefBind {
fdata: fdata.clone(),
- scope,
- node: multi_def_bind,
+ node,
+ scope: scopeid,
}),
);
}
+ let ok = errors.is_empty();
emit_errors(&fdata, errors);
+ ok
},
- JobType::ResolveDefBind { fdata, scope, node } => {},
+ JobType::ResolveDefBind { fdata, node, scope } => {
+ todo!("resolving is yet to be implemented");
+ true
+ },
+ };
+ if !result {
+ ok = false;
}
if let Some((_, deps)) = c_state.deps.remove(&job.id) {
@@ -382,14 +445,9 @@ fn worker_loop(
}
}
- if c_state.njobs.fetch_sub(1, Ordering::Release) == 1 {
- // njobs is 0; wake all threads so they can observe the termination
- // condition and exit.
+ if c_state.job_complete() == 0 {
c_state.wake_all();
-
- // break here to avoid unnecessary steal attempts after work is
- // done.
- break;
+ return ok;
}
}
}
diff --git a/oryxc/src/hashtrie.rs b/oryxc/src/hashtrie.rs
new file mode 100644
index 0000000..87c561e
--- /dev/null
+++ b/oryxc/src/hashtrie.rs
@@ -0,0 +1,219 @@
+use std::hash::{
+ Hash,
+ Hasher,
+};
+use std::ptr;
+use std::sync::atomic::{
+ AtomicPtr,
+ Ordering,
+};
+
+use crate::arena::LocalArena;
+
+struct FNV1a {
+ state: u64,
+}
+
+impl FNV1a {
+ const OFFSET_BASIS: u64 = 0xCBF29CE484222325;
+ const PRIME: u64 = 0x00000100000001B3;
+
+ fn new() -> Self {
+ return Self {
+ state: Self::OFFSET_BASIS,
+ };
+ }
+}
+
+impl Hasher for FNV1a {
+ fn write(&mut self, bytes: &[u8]) {
+ for &b in bytes {
+ self.state ^= b as u64;
+ self.state = self.state.wrapping_mul(Self::PRIME);
+ }
+ }
+
+ fn finish(&self) -> u64 {
+ return self.state;
+ }
+}
+
+pub struct HTrieNode<K, V>
+where
+ K: Copy + Eq + Hash,
+{
+ sub: [AtomicPtr<HTrieNode<K, V>>; 4],
+ key: K,
+ val: AtomicPtr<V>,
+}
+
+impl<K, V> HTrieNode<K, V>
+where
+ K: Copy + Eq + Hash,
+{
+ fn new(key: K, valptr: *mut V) -> Self {
+ return Self {
+ sub: [
+ AtomicPtr::new(ptr::null_mut()),
+ AtomicPtr::new(ptr::null_mut()),
+ AtomicPtr::new(ptr::null_mut()),
+ AtomicPtr::new(ptr::null_mut()),
+ ],
+ key,
+ val: AtomicPtr::new(valptr),
+ };
+ }
+}
+
+#[derive(Debug)]
+pub struct HTrie<K, V>
+where
+ K: Copy + Eq + Hash,
+{
+ root: AtomicPtr<HTrieNode<K, V>>,
+}
+
+impl<K, V> HTrie<K, V>
+where
+ K: Copy + Eq + Hash,
+{
+ pub fn new() -> Self {
+ return Self {
+ root: AtomicPtr::new(ptr::null_mut()),
+ };
+ }
+
+ /// Lock-free insert into the hash-trie.
+ ///
+ /// Returns `None` if the key was newly inserted.
+ /// Returns `Some(val)` with the previous value if the key already
+ /// existed.
+ pub fn insert(&self, key: K, val: V, arena: &LocalArena) -> Option<V> {
+ let mut h = {
+ let mut h = FNV1a::new();
+ key.hash(&mut h);
+ h.finish()
+ };
+
+ let mut m = &self.root;
+ let valptr = arena.alloc(val);
+
+ loop {
+ let mut n = m.load(Ordering::Acquire);
+
+ if n.is_null() {
+ let mark = arena.mark();
+ let node = arena.alloc(HTrieNode::new(key, valptr));
+
+ match m.compare_exchange(
+ ptr::null_mut(),
+ node as *mut HTrieNode<K, V>,
+ Ordering::Release,
+ Ordering::Acquire,
+ ) {
+ Ok(_) => {
+ return None;
+ },
+ Err(ptr) => {
+ arena.restore(mark);
+ n = ptr;
+ },
+ }
+ }
+
+ let n = unsafe { &*n };
+
+ if n.key == key {
+ let old_valptr = n.val.swap(valptr, Ordering::AcqRel);
+
+ if old_valptr.is_null() {
+ return None;
+ } else {
+ let old_val = unsafe { ptr::read(old_valptr) };
+ return Some(old_val);
+ }
+ }
+
+ m = &n.sub[(h >> 62) as usize];
+ h <<= 2;
+ }
+ }
+
+ /// Check if the given key exists in the hash-trie.
+ pub fn contains(&self, key: K) -> bool {
+ let mut h = {
+ let mut h = FNV1a::new();
+ key.hash(&mut h);
+ h.finish()
+ };
+
+ let mut m = &self.root;
+
+ loop {
+ let n = m.load(Ordering::Acquire);
+ if n.is_null() {
+ return false;
+ }
+ let n = unsafe { &*n };
+ if n.key == key {
+ return !n.val.load(Ordering::Acquire).is_null();
+ }
+ m = &n.sub[(h >> 62) as usize];
+ h <<= 2;
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::arena::{
+ GlobalArena,
+ LocalArena,
+ };
+ use crate::hashtrie::HTrie;
+
+ #[test]
+ fn test_htrie_insert() {
+ let ga = GlobalArena::new(128);
+ let la = LocalArena::new(&ga);
+ let ht = HTrie::new();
+
+ assert_eq!(ht.insert("foo", "bar", &la), None);
+ assert_eq!(ht.insert("hello", "sailor", &la), None);
+ assert_eq!(ht.insert("thomas", "voss", &la), None);
+ }
+
+ #[test]
+ fn test_htrie_overwrite() {
+ let ga = GlobalArena::new(128);
+ let la = LocalArena::new(&ga);
+ let ht = HTrie::new();
+
+ ht.insert("foo", "bar", &la);
+ ht.insert("hello", "sailor", &la);
+ ht.insert("thomas", "voss", &la);
+ assert_eq!(ht.insert("foo", "bar-0", &la), Some("bar"));
+ assert_eq!(ht.insert("hello", "sailor-0", &la), Some("sailor"));
+ assert_eq!(ht.insert("thomas", "voss-0", &la), Some("voss"));
+ assert_eq!(ht.insert("foo", "bar-1", &la), Some("bar-0"));
+ assert_eq!(ht.insert("hello", "sailor-1", &la), Some("sailor-0"));
+ assert_eq!(ht.insert("thomas", "voss-1", &la), Some("voss-0"));
+ }
+
+ #[test]
+ fn test_htrie_contains() {
+ let ga = GlobalArena::new(128);
+ let la = LocalArena::new(&ga);
+ let ht = HTrie::new();
+
+ assert!(!ht.contains("foo"));
+ assert!(!ht.contains("hello"));
+ assert!(!ht.contains("thomas"));
+ ht.insert("foo", "bar", &la);
+ ht.insert("hello", "sailor", &la);
+ ht.insert("thomas", "voss", &la);
+ assert!(ht.contains("foo"));
+ assert!(ht.contains("hello"));
+ assert!(ht.contains("thomas"));
+ }
+}
diff --git a/oryxc/src/lexer.rs b/oryxc/src/lexer.rs
index 09e2881..2dace02 100644
--- a/oryxc/src/lexer.rs
+++ b/oryxc/src/lexer.rs
@@ -56,9 +56,11 @@ pub enum TokenType {
Identifier,
KeywordDef,
KeywordFunc,
+ KeywordModule,
KeywordReturn,
Number,
Percent2,
+ SlashPercent,
String,
}
@@ -219,6 +221,13 @@ pub fn tokenize(s: &str) -> Result<Soa<Token>, OryxError> {
view: (i, j + 1),
})
},
+ '/' if ctx.peek().is_some_and(|c| c == '%') => {
+ ctx.next(); /* Consume ‘/’ */
+ Some(Token {
+ kind: TokenType::SlashPercent,
+ view: (i, j + 1),
+ })
+ },
'!' | '&' | '(' | ')' | '*' | '+' | ',' | '-' | '/' | ';' | '<'
| '=' | '>' | '[' | ']' | '^' | '{' | '|' | '}' | '~' | '…'
| '%' => Some(Token {
diff --git a/oryxc/src/main.rs b/oryxc/src/main.rs
index 636b8ed..e3523bd 100644
--- a/oryxc/src/main.rs
+++ b/oryxc/src/main.rs
@@ -3,6 +3,7 @@
mod arena;
mod compiler;
mod errors;
+mod hashtrie;
mod intern;
mod lexer;
mod parser;
@@ -29,10 +30,10 @@ pub struct Flags {
#[derive(Parser)]
struct Args {
- #[arg(short = 'l', long)]
+ #[arg(long)]
debug_lexer: bool,
- #[arg(short = 'p', long)]
+ #[arg(long)]
debug_parser: bool,
#[arg(short = 's', long, default_value = "standard")]
@@ -78,7 +79,7 @@ fn main() {
fn usage() -> String {
format!(
concat!(
- "Usage: {0} [-lp] [-s oneline|standard] [-t threads]\n",
+ "Usage: {0} [-s oneline|standard] [-t threads]\n",
" {0} -h",
),
errors::progname().display()
diff --git a/oryxc/src/parser.rs b/oryxc/src/parser.rs
index 89e2769..629b290 100644
--- a/oryxc/src/parser.rs
+++ b/oryxc/src/parser.rs
@@ -44,6 +44,12 @@ pub struct AstNode {
pub sub: SubNodes,
}
+#[derive(Debug)]
+pub struct Ast {
+ pub nodes: Soa<AstNode>,
+ pub extra: Vec<u32>,
+}
+
struct Parser<'a> {
ast: Soa<AstNode>,
extra_data: Vec<u32>,
@@ -536,6 +542,15 @@ impl<'a> Parser<'a> {
self.next(); /* Consume ‘(’ */
return self.scratch_guard(|p| {
let lhs = p.parse_decl_list()?;
+ if let Some(&ty) = p.scratch[lhs..].last() {
+ if ty == u32::MAX {
+ let i = p.scratch.len() as u32 - 2;
+ return Err(OryxError::new(
+ p.get_view_at(i),
+ "function parameter has no declared type",
+ ));
+ }
+ }
if p.get_n_move() != TokenType::ParenR {
/* TODO: Highlight the entire argument list */
@@ -568,7 +583,7 @@ impl<'a> Parser<'a> {
_ => p.scratch.len(),
};
- let nargs = rhs - lhs;
+ let nargs = (rhs - lhs) / 2;
let nrets = p.scratch.len() - rhs;
let argbeg = p.extra_data.len();
let retbeg = argbeg + nargs * 2 + 1;
@@ -686,6 +701,7 @@ impl<'a> Parser<'a> {
| TokenType::Asterisk
| TokenType::Percent
| TokenType::Percent2
+ | TokenType::SlashPercent
| TokenType::Slash => 5,
TokenType::Bar
| TokenType::Minus
@@ -817,6 +833,7 @@ impl<'a> Parser<'a> {
| TokenType::Percent2
| TokenType::Plus
| TokenType::Slash
+ | TokenType::SlashPercent
| TokenType::Tilde => {
let i = self.cursor;
self.next();
@@ -883,9 +900,7 @@ impl<'a> Parser<'a> {
}
}
-pub fn parse(
- tokens: &Soa<Token>,
-) -> Result<(Soa<AstNode>, Vec<u32>), Vec<OryxError>> {
+pub fn parse(tokens: &Soa<Token>) -> Result<Ast, Vec<OryxError>> {
let mut p = Parser::new(tokens);
while p.get() != TokenType::Eof {
p.parse_toplevel();
@@ -902,5 +917,8 @@ pub fn parse(
tok: 0,
sub: SubNodes(stmtsbeg as u32, nstmts as u32),
});
- return Ok((p.ast, p.extra_data));
+ return Ok(Ast {
+ nodes: p.ast,
+ extra: p.extra_data,
+ });
}
diff --git a/oryxc/src/prelude.rs b/oryxc/src/prelude.rs
index 32a123f..4e36f33 100644
--- a/oryxc/src/prelude.rs
+++ b/oryxc/src/prelude.rs
@@ -4,6 +4,8 @@ use std::fmt::{
Formatter,
};
+use crate::hashtrie::HTrie;
+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct FileId(pub usize);
@@ -11,14 +13,32 @@ pub struct FileId(pub usize);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct SymbolId(pub u32);
+#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ScopeId(pub usize);
impl ScopeId {
pub const GLOBAL: Self = Self(0);
+ pub const INVALID: Self = Self(usize::MAX);
}
-#[derive(Default)]
+#[derive(Debug)]
+pub struct Scope {
+ pub parent: ScopeId,
+ pub symtab: HTrie<SymbolId, Symbol>,
+}
+
+impl Scope {
+ pub fn new(parent: ScopeId) -> Self {
+ return Self {
+ parent,
+ symtab: HTrie::new(),
+ };
+ }
+}
+
+#[repr(u8)]
+#[derive(Debug, Default)]
pub enum ResolutionState {
#[default]
Unresolved,
@@ -27,14 +47,22 @@ pub enum ResolutionState {
Poisoned,
}
-#[derive(Default)]
+#[derive(Debug)]
pub struct Symbol {
- pub state: ResolutionState,
- pub r#type: u32,
+ pub state: ResolutionState,
+ pub kind: SymbolType,
+}
+
+#[repr(u8)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum SymbolType {
+ Constant,
+ FuncParam,
}
pub enum OryxType {
Integer { bits: usize, signed: bool },
+ Boolean,
Pointer { base: u32 },
Function { args: Vec<u32>, rets: Vec<u32> },
}
diff --git a/test.xy b/test.yx
index a908c0e..b057812 100644
--- a/test.xy
+++ b/test.yx
@@ -21,7 +21,7 @@ def foo = func() {
def main′ = func() {
puts("Hello, sailor!");
some_func(#b10.1100'1001e+11);
- x, y = 69, 420;
+ x, y = 69, 420;
slices_sort(my_slice, func(x, y int) int {
return x - y;
});