Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit bdf6c42872adfa98da527f5049da989c20825200 0 parents
@tj tj authored
3  .gitignore
@@ -0,0 +1,3 @@
+build
+*.o
+luna
0  History.md
No changes.
19 Makefile
@@ -0,0 +1,19 @@
+
+SRC = $(shell find src/*.c)
+OBJ = ${SRC:.c=.o}
+
+PREFIX = /usr/local
+CC = clang
+CFLAGS = -Wall -std=c99
+
+luna: $(OBJ)
+ @mkdir -p build
+ $(CC) $^ -o $@
+
+%.o: %.c
+ $(CC) -c $(CFLAGS) $< -o $@
+
+clean:
+ rm -f luna $(OBJ)
+
+.PHONY: clean
56 Readme.md
@@ -0,0 +1,56 @@
+
+# Luna
+
+ The Luna programming language.
+
+## About
+
+ Luna is an expressive, minimalistic, elegant programming language with reactor based concurrency at the core, or at least it will be :). This project is _very_ much a work in progress, as I explore the wonderful world of VMs!
+
+## Goals
+
+ - simple, small, elegant, explicit
+ - prototypal inheritance
+ - reflection capabilities
+ - reactor concurrency model (event loop)
+ - stack based VM
+ - ...
+
+## Build
+
+ To build Luna, simply run:
+
+ $ make
+
+ When successful you may then execute the binary:
+
+ $ ./luna --help
+
+## Status
+
+ I _just_ started Luna, so don't expect much yet! first up is lexical analysis; currently Luna will just spit out debugging information, nothing fancy.
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2011 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
309 src/lexer.c
@@ -0,0 +1,309 @@
+
+//
+// lexer.c
+//
+// Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
+//
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "lexer.h"
+
+#define next (self->stash = fgetc(self->stream))
+#define undo ungetc(self->stash, self->stream)
+#define token(t) self->tok.type = LUNA_TOKEN_##t
+
+/*
+ * Initialize lexer with the given `stream` and `filename`.
+ */
+
+void
+luna_lexer_init(luna_lexer_t *self, FILE *stream, const char *filename) {
+ self->error = NULL;
+ self->stream = stream;
+ self->filename = filename;
+ self->indents = 0;
+ self->outdents = 0;
+ self->lineno = 1;
+}
+
+/*
+ * Set error `msg` and token to ILLEGAL.
+ */
+
+static void
+error(luna_lexer_t *self, char *msg) {
+ self->error = msg;
+ token(ILLEGAL);
+}
+
+/*
+ * Convert hex digit `c` to an integer,
+ * returning -1 on failure.
+ */
+
+static int
+hex(const char c) {
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
+ return -1;
+}
+
+/*
+ * Accept char `c`, returning it, or 0 otherwise.
+ */
+
+static int
+accept(luna_lexer_t *self, int c) {
+ if (c == next) return c;
+ undo;
+ return 0;
+}
+
+/*
+ * Perform an outdent.
+ */
+
+static int
+outdent(luna_lexer_t *self) {
+ --self->outdents;
+ return token(OUTDENT);
+}
+
+/*
+ * Scan newline, and determine if we have
+ * an indentation.
+ */
+
+static int
+scan_newline(luna_lexer_t *self) {
+ int curr = 0;
+ int prev = self->indent_stack[self->indents];
+
+ ++self->lineno;
+
+ while (accept(self, ' ')) ++curr;
+
+ if (curr > prev) {
+ token(INDENT);
+ } else if (curr < prev) {
+ while (self->indent_stack[self->indents] > curr) {
+ ++self->outdents;
+ --self->indents;
+ }
+ outdent(self);
+ } else {
+ token(NEWLINE);
+ }
+
+ self->indent_stack[++self->indents] = curr;
+ return 1;
+}
+
+/*
+ * Scan identifier.
+ */
+
+static int
+scan_ident(luna_lexer_t *self, int c) {
+ int len = 0;
+ char buf[128]; // TODO: ditch these buffers
+ token(ID);
+
+ do {
+ buf[len++] = c;
+ } while (isalpha(c = next) || isdigit(c) || '_' == c);
+
+ buf[len++] = 0;
+ self->tok.value.as_string = strdup(buf); // TODO: remove
+ undo;
+ return 1;
+}
+
+/*
+ * Scan string hex literal, returning -1 on invalid digits.
+ */
+
+static int
+hex_literal(luna_lexer_t *self) {
+ int a = hex(next)
+ , b = hex(next);
+
+ if (a > -1 && b > -1) return a << 4 | b;
+
+ error(self, "string hex literal \\x contains invalid digits");
+ return -1;
+}
+
+/*
+ * Scan string.
+ */
+
+static int
+scan_string(luna_lexer_t *self, int quote) {
+ int c, len = 0;
+ char buf[128];
+ token(STRING);
+
+ while (quote != (c = next)) {
+ switch (c) {
+ case '\n': ++self->lineno; break;
+ case '\\':
+ switch (c = next) {
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 'e': c = '\e'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = '\v'; break;
+ case 'x':
+ if (-1 == (c = hex_literal(self)))
+ return 0;
+ }
+ break;
+ }
+ buf[len++] = c;
+ }
+
+ buf[len++] = 0;
+ self->tok.value.as_string = strdup(buf); // TODO: remove
+ return 1;
+}
+
+/*
+ * Scan number.
+ */
+
+static int
+scan_number(luna_lexer_t *self, int c) {
+ // TODO: exponential notation
+ int n = 0;
+ token(INT);
+
+ switch (c) {
+ case '0': goto scan_hex;
+ case '.': goto scan_float;
+ default: goto scan_int;
+ }
+
+scan_hex:
+ switch (c = next) {
+ case 'x':
+ if (!isxdigit(c = next)) {
+ error(self, "hex literal expects one or more digits");
+ return 0;
+ } else {
+ do n = n << 4 | hex(c);
+ while (isxdigit(c = next));
+ }
+ self->tok.value.as_int = n;
+ undo;
+ return 1;
+ default:
+ goto scan_int;
+ }
+
+// [0-9_]+
+
+scan_int:
+ do {
+ if ('_' == c) continue;
+ else if ('.' == c) goto scan_float;
+ n = n * 10 + c - '0';
+ } while (isdigit(c = next) || '_' == c || '.' == c);
+ undo;
+ self->tok.value.as_int = n;
+ return 1;
+
+// [0-9_]+
+
+scan_float: {
+ int e = 1;
+ token(FLOAT);
+ while (isdigit(c = next) || '_' == c) {
+ if ('_' == c) continue;
+ n = n * 10 + c - '0';
+ e *= 10;
+ }
+ undo;
+ self->tok.value.as_float = (float) n / e;
+}
+
+ return 1;
+}
+
+/*
+ * Scan the next token in the stream, returns 0
+ * on EOS, ILLEGAL token, or a syntax error.
+ */
+
+int
+luna_lexer_next(luna_lexer_t *self) {
+ int c;
+ token(ILLEGAL);
+
+ // deferred outdents
+ if (self->outdents) return outdent(self);
+
+ // scan
+ switch (c = next) {
+ case '(': return token(LPAREN);
+ case ')': return token(RPAREN);
+ case '{': return token(LBRACE);
+ case '}': return token(RBRACE);
+ case '[': return token(LBRACK);
+ case ']': return token(RBRACK);
+ case ':': return token(COLON);
+ case ';': return token(SEMICOLON);
+ case ',': return token(COMMA);
+ case '+': return token(OP_PLUS);
+ case '-': return token(OP_MINUS);
+ case '*': return token(OP_MULT);
+ case '/': return token(OP_DIV);
+ case '%': return token(OP_MOD);
+ case '^': return token(OP_BIT_XOR);
+ case '~': return token(OP_BIT_NOT);
+ case '!':
+ return '=' == next
+ ? token(OP_NEQ)
+ : (undo, token(OP_NOT));
+ case '=':
+ return '=' == next
+ ? token(OP_EQ)
+ : (undo, token(OP_ASSIGN));
+ case '<':
+ return '=' == next
+ ? token(OP_LTE)
+ : (undo, token(OP_LT));
+ case '>':
+ return '=' == next
+ ? token(OP_GTE)
+ : (undo, token(OP_GT));
+ case '&':
+ return '&' == next
+ ? token(OP_AND)
+ : (undo, token(OP_BIT_AND));
+ case '|':
+ return '|' == next
+ ? token(OP_OR)
+ : (undo, token(OP_BIT_OR));
+ case '\n':
+ return scan_newline(self);
+ case '"':
+ case '\'':
+ return scan_string(self, c);
+ case EOF:
+ token(EOS);
+ return 0;
+ default:
+ if (isalpha(c) || '_' == c) return scan_ident(self, c);
+ if (isdigit(c) || '.' == c) return scan_number(self, c);
+ error(self, "illegal character");
+ return 0;
+ }
+}
41 src/lexer.h
@@ -0,0 +1,41 @@
+
+//
+// lexer.h
+//
+// Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
+//
+
+#ifndef __LUNA_LEXER__
+#define __LUNA_LEXER__
+
+#include <stdio.h>
+#include "token.h"
+
+#ifndef LUNA_BUF_SIZE
+#define LUNA_BUF_SIZE 1024
+#endif
+
+#ifndef LUNA_MAX_INDENTS
+#define LUNA_MAX_INDENTS 32
+#endif
+
+typedef struct {
+ char *error;
+ int stash;
+ int lineno;
+ int indent_stack[LUNA_MAX_INDENTS];
+ int indents;
+ int outdents;
+ FILE *stream;
+ const char *filename;
+ luna_token_t tok;
+ char buf[LUNA_BUF_SIZE];
+} luna_lexer_t;
+
+int
+luna_lexer_next(luna_lexer_t *self);
+
+void
+luna_lexer_init(luna_lexer_t *self, FILE *stream, const char *filename);
+
+#endif /* __LUNA_LEXER__ */
101 src/luna.c
@@ -0,0 +1,101 @@
+
+//
+// luna.c
+//
+// Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
+//
+
+#include <stdlib.h>
+#include <string.h>
+#include "luna.h"
+#include "lexer.h"
+
+/*
+ * Output usage information.
+ */
+
+void
+usage() {
+ fprintf(stderr,
+ "\n Usage: luna [options]"
+ "\n"
+ "\n Options:"
+ "\n"
+ "\n -h, --help output help information"
+ "\n -V, --version output luna version"
+ "\n"
+ "\n"
+ );
+ exit(1);
+}
+
+/*
+ * Output luna version.
+ */
+
+void
+version() {
+ printf("%s\n", LUNA_VERSION);
+ exit(0);
+}
+
+/*
+ * Parse arguments.
+ */
+
+void
+parse_args(int argc, const char **argv) {
+ const char *arg;
+ for (int i = 0; i < argc; ++i) {
+ arg = argv[i];
+ if (0 == strcmp(arg, "-h") || 0 == strcmp(arg, "--help"))
+ usage();
+ else if (0 == strcmp(arg, "-V") || 0 == strcmp(arg, "--version"))
+ version();
+ else if ('-' == arg[0]) {
+ fprintf(stderr, "unknown flag %s\n", arg);
+ exit(1);
+ }
+ }
+}
+
+/*
+ * Parse arguments and scan from stdin (for now).
+ */
+
+int
+main(int argc, const char **argv){
+ parse_args(argc, argv);
+ luna_lexer_t lex;
+ luna_lexer_init(&lex, stdin, "stdin");
+
+ while (luna_lexer_next(&lex)) {
+ switch (lex.tok.type) {
+ case LUNA_TOKEN_INT:
+ printf("integer %d\n", lex.tok.value.as_int);
+ break;
+ case LUNA_TOKEN_FLOAT:
+ printf("float %f\n", lex.tok.value.as_float);
+ break;
+ case LUNA_TOKEN_STRING:
+ printf("string '%s'\n", lex.tok.value.as_string);
+ break;
+ case LUNA_TOKEN_ID:
+ printf("identifier %s\n", lex.tok.value.as_string);
+ break;
+ default:
+ printf("%s\n", luna_token_type_string(lex.tok.type));
+ break;
+ }
+ }
+
+ if (lex.tok.type != LUNA_TOKEN_EOS) {
+ fprintf(stderr
+ , "luna(%s:%d): SyntaxError %s\n"
+ , lex.filename
+ , lex.lineno
+ , lex.error);
+ }
+
+ return 0;
+}
17 src/luna.h
@@ -0,0 +1,17 @@
+
+//
+// luna.h
+//
+// Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
+//
+
+#ifndef __LUNA__
+#define __LUNA__
+
+/*
+ * Luna version.
+ */
+
+#define LUNA_VERSION "0.0.1"
+
+#endif /* __LUNA__ */
98 src/token.h
@@ -0,0 +1,98 @@
+
+//
+// token.h
+//
+// Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
+//
+
+#ifndef __LUNA_TOKEN__
+#define __LUNA_TOKEN__
+
+/*
+ * Tokens.
+ */
+
+#define LUNA_TOKEN_LIST \
+ t(ILLEGAL, "illegal") \
+ t(EOS, "end-of-source") \
+ t(NEWLINE, "newline") \
+ t(INDENT, "indent") \
+ t(OUTDENT, "outdent") \
+ t(ID, "identifier") \
+ t(INT, "int") \
+ t(FLOAT, "float") \
+ t(STRING, "string") \
+ t(LBRACE, "{") \
+ t(RBRACE, "}") \
+ t(LPAREN, "(") \
+ t(RPAREN, ")") \
+ t(LBRACK, "[") \
+ t(RBRACK, "]") \
+ t(COLON, ":") \
+ t(SEMICOLON, ";") \
+ t(COMMA, ",") \
+ t(OP_ASSIGN, "=") \
+ t(OP_NOT, "!") \
+ t(OP_PLUS, "+") \
+ t(OP_MINUS, "-") \
+ t(OP_MULT, "*") \
+ t(OP_DIV, "/") \
+ t(OP_MOD, "%") \
+ t(OP_GT, ">") \
+ t(OP_LT, "<") \
+ t(OP_GTE, ">=") \
+ t(OP_LTE, "<=") \
+ t(OP_EQ, "==") \
+ t(OP_NEQ, "!=") \
+ t(OP_AND, "&&") \
+ t(OP_OR, "||") \
+ t(OP_BIT_AND, "&") \
+ t(OP_BIT_OR, "|") \
+ t(OP_BIT_XOR, "^") \
+ t(OP_BIT_NOT, "~")
+
+/*
+ * Tokens enum.
+ */
+
+typedef enum {
+#define t(tok, str) LUNA_TOKEN_##tok,
+LUNA_TOKEN_LIST
+#undef t
+} luna_token;
+
+/*
+ * Token strings.
+ */
+
+static char *luna_token_strings[] = {
+#define t(tok, str) str,
+LUNA_TOKEN_LIST
+#undef t
+};
+
+/*
+ * Token struct.
+ */
+
+typedef struct {
+ int len;
+ luna_token type;
+ struct {
+ char *as_string;
+ float as_float;
+ int as_int;
+ } value;
+} luna_token_t;
+
+/*
+ * Return the string associated with the
+ * given token `type`.
+ */
+
+static inline const char *
+luna_token_type_string(luna_token type) {
+ return luna_token_strings[type];
+}
+
+#endif /* __LUNA_TOKEN__ */

2 comments on commit bdf6c42

@marcuswestin

you may not care, and probably shouldn't:

this is a bit of a namespace collision with http://asana.com/luna/. They decided not to continue development of lunascript but rather simply stick with the framework luna.

fyi :)

best of luck

@tj
Owner
tj commented on bdf6c42

doh! I saw a long time ago that they had "lunascript" and I was like heyyyyy I always wanted to call a language that, good to know

Please sign in to comment.
Something went wrong with that request. Please try again.