From b59fa2da28e13d4b38ff799bc330dd6291a49ff7 Mon Sep 17 00:00:00 2001 From: mux Date: Mon, 6 Jan 2014 06:41:56 +0200 Subject: [PATCH 01/18] Fix LED pin enum * Fix LED pin enum, first one should start at 1 * Fix LED initialization typo --- stm/led.c | 4 ++-- stm/led.h | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/stm/led.c b/stm/led.c index 9809c2177..19ce99cec 100644 --- a/stm/led.c +++ b/stm/led.c @@ -63,8 +63,8 @@ void led_init(void) { /* Turn off LEDs */ PYB_LED_OFF(PYB_LED1_PORT, PYB_LED1_PIN); PYB_LED_OFF(PYB_LED2_PORT, PYB_LED2_PIN); - PYB_LED_OFF(PYB_LED3_PORT, PYB_LED1_PIN); - PYB_LED_OFF(PYB_LED4_PORT, PYB_LED2_PIN); + PYB_LED_OFF(PYB_LED3_PORT, PYB_LED3_PIN); + PYB_LED_OFF(PYB_LED4_PORT, PYB_LED4_PIN); /* Initialize LEDs */ GPIO_InitStructure.GPIO_Pin = PYB_LED1_PIN; diff --git a/stm/led.h b/stm/led.h index fcbfa1763..c68f3e414 100644 --- a/stm/led.h +++ b/stm/led.h @@ -1,13 +1,13 @@ typedef enum { - PYB_LED_R1 = 0, - PYB_LED_R2 = 1, - PYB_LED_G1 = 2, - PYB_LED_G2 = 3, + PYB_LED_R1 = 1, + PYB_LED_R2 = 2, + PYB_LED_G1 = 3, + PYB_LED_G2 = 4, //STM32F4DISC - PYB_LED_R = 0, - PYB_LED_G = 1, - PYB_LED_B = 2, - PYB_LED_O = 3, + PYB_LED_R = 1, + PYB_LED_G = 2, + PYB_LED_B = 3, + PYB_LED_O = 4, } pyb_led_t; void led_init(void); From b6e9c7c6970971cc0aeadd38cdf287352c97ddf6 Mon Sep 17 00:00:00 2001 From: Dave Hylands Date: Mon, 6 Jan 2014 00:20:11 -0800 Subject: [PATCH 02/18] Initial support for Teensy 3.1 --- teensy/Makefile | 155 ++++++++++++++ teensy/README.md | 31 +++ teensy/gchelper.s | 36 ++++ teensy/led.c | 86 ++++++++ teensy/led.h | 9 + teensy/lexerteensy.c | 33 +++ teensy/lexerteensy.h | 8 + teensy/main.c | 474 ++++++++++++++++++++++++++++++++++++++++++ teensy/main.cpp | 245 ++++++++++++++++++++++ teensy/malloc0.c | 57 +++++ teensy/mk20dx256.ld | 164 +++++++++++++++ teensy/mpconfigport.h | 21 ++ teensy/printf.c | 305 +++++++++++++++++++++++++++ teensy/std.h | 22 ++ teensy/string0.c | 110 ++++++++++ teensy/usb.c | 44 ++++ teensy/usb.h | 9 + 17 files changed, 1809 insertions(+) create mode 100644 teensy/Makefile create mode 100644 teensy/README.md create mode 100644 teensy/gchelper.s create mode 100644 teensy/led.c create mode 100644 teensy/led.h create mode 100644 teensy/lexerteensy.c create mode 100644 teensy/lexerteensy.h create mode 100644 teensy/main.c create mode 100644 teensy/main.cpp create mode 100644 teensy/malloc0.c create mode 100644 teensy/mk20dx256.ld create mode 100644 teensy/mpconfigport.h create mode 100644 teensy/printf.c create mode 100644 teensy/std.h create mode 100644 teensy/string0.c create mode 100644 teensy/usb.c create mode 100644 teensy/usb.h diff --git a/teensy/Makefile b/teensy/Makefile new file mode 100644 index 000000000..98b467a06 --- /dev/null +++ b/teensy/Makefile @@ -0,0 +1,155 @@ +ifeq ($(ARDUINO),) +$(error Please define ARDUINO (where TeensyDuino is installed)) +endif +TOOLS_PATH = $(ARDUINO)/hardware/tools +COMPILER_PATH = $(TOOLS_PATH)/arm-none-eabi/bin +CORE_PATH = $(ARDUINO)/hardware/teensy/cores/teensy3 + +PYSRC=../py +BUILD=build + +AS = $(COMPILER_PATH)/arm-none-eabi-as +CC = $(COMPILER_PATH)/arm-none-eabi-gcc +LD = $(COMPILER_PATH)/arm-none-eabi-ld +OBJCOPY = $(COMPILER_PATH)/arm-none-eabi-objcopy +SIZE = $(COMPILER_PATH)/arm-none-eabi-size + +CFLAGS_TEENSY = -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -D__MK20DX256__ +CFLAGS_CORTEX_M4 = -mthumb -mtune=cortex-m4 -mcpu=cortex-m4 -fsingle-precision-constant -Wdouble-promotion $(CFLAGS_TEENSY) +CFLAGS = -I. -I$(PYSRC) -I$(CORE_PATH) -Wall -ansi -std=gnu99 -Os -DNDEBUG $(CFLAGS_CORTEX_M4) +LDFLAGS = -nostdlib -T mk20dx256.ld +LIBS = -L $(COMPILER_PATH)/../lib/gcc/arm-none-eabi/4.7.2/thumb2 -lgcc + +SRC_C = \ + main.c \ + lexerteensy.c \ + led.c \ + malloc0.c \ + printf.c \ + string0.c \ + usb.c \ + +SRC_S = \ + gchelper.s \ + +PY_O = \ + nlrthumb.o \ + gc.o \ + malloc.o \ + qstr.o \ + vstr.o \ + unicode.o \ + lexer.o \ + parse.o \ + scope.o \ + compile.o \ + emitcommon.o \ + emitpass1.o \ + emitbc.o \ + asmthumb.o \ + emitnthumb.o \ + emitinlinethumb.o \ + runtime.o \ + map.o \ + obj.o \ + objbool.o \ + objboundmeth.o \ + objcell.o \ + objclass.o \ + objclosure.o \ + objcomplex.o \ + objdict.o \ + objexcept.o \ + objfloat.o \ + objfun.o \ + objgenerator.o \ + objinstance.o \ + objint.o \ + objlist.o \ + objmodule.o \ + objnone.o \ + objrange.o \ + objset.o \ + objslice.o \ + objstr.o \ + objtuple.o \ + objtype.o \ + builtin.o \ + builtinimport.o \ + vm.o \ + showbc.o \ + repl.o \ + +SRC_TEENSY = \ + mk20dx128.c \ + pins_teensy.c \ + analog.c \ + usb_desc.c \ + usb_dev.c \ + usb_mem.c \ + usb_serial.c \ + yield.c \ + +OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(SRC_S:.s=.o) $(PY_O) $(SRC_TEENSY:.c=.o)) +#LIB = -lreadline +# the following is needed for BSD +#LIB += -ltermcap +PROG = micropython + +all: hex +hex: $(PROG).hex + +post_compile: $(PROG).hex + $(TOOLS_PATH)/teensy_post_compile -file="$(basename $<)" -path="$(CURDIR)" -tools="$(TOOLS_PATH)" + +reboot: + -$(TOOLS_PATH)/teensy_reboot + +upload: post_compile reboot + +$(PROG).elf: $(BUILD) $(OBJ) + $(CC) $(LDFLAGS) -o "$@" -Wl,-Map,$(PROG).map $(OBJ) $(LIBS) + +%.hex: %.elf + $(SIZE) "$<" + $(OBJCOPY) -O ihex -R .eeprom "$<" "$@" + +$(BUILD): + mkdir -p $@ + +$(BUILD)/%.o: %.s + $(AS) -o $@ $< + +$(BUILD)/%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(BUILD)/%.o: $(PYSRC)/%.S + $(CC) $(CFLAGS) -c -o $@ $< + +$(BUILD)/%.o: $(PYSRC)/%.c mpconfigport.h + $(CC) $(CFLAGS) -c -o $@ $< + +$(BUILD)/%.o: $(CORE_PATH)/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(BUILD)/emitnthumb.o: $(PYSRC)/emitnative.c $(PYSRC)/emit.h + $(CC) $(CFLAGS) -DN_THUMB -c -o $@ $< + +# optimising gc for speed; 5ms down to 4ms +$(BUILD)/gc.o: $(PYSRC)/gc.c + $(CC) $(CFLAGS) -O3 -c -o $@ $< + +# optimising vm for speed, adds only a small amount to code size but makes a huge difference to speed (20% faster) +$(BUILD)/vm.o: $(PYSRC)/vm.c + $(CC) $(CFLAGS) -O3 -c -o $@ $< + +$(BUILD)/main.o: mpconfigport.h +$(BUILD)/parse.o: $(PYSRC)/grammar.h +$(BUILD)/compile.o: $(PYSRC)/grammar.h +$(BUILD)/emitbc.o: $(PYSRC)/emit.h + +clean: + /bin/rm -rf $(BUILD) + /bin/rm -f $(PROG).elf $(PROG).hex $(PROG).map + +.PHONY: clean diff --git a/teensy/README.md b/teensy/README.md new file mode 100644 index 000000000..31339cd79 --- /dev/null +++ b/teensy/README.md @@ -0,0 +1,31 @@ +Build Instructions for Teensy 3.1 + +This assumes that you have TeensyDuino installed and set the ARUINO environment +variable pointing to the where Arduino with TeensyDuino is installed. + +``` +cd teensy +ARDUINO=~/arduino-1.0.5 make +``` + +To build the loader + +``` +cd teensy/loader +make +``` + +To upload micropython to the Teensy 3.1. + +Press the Program button on the Teensy 3.1 +``` +make upload +``` + +Currently, the python prompt is through the USB serial interface. + +The LED will blink (100 msec on/100 msec off) while waiting for the USB Serial +device to be configured, and will blink (200 msec on/200 msec off) while +sitting at the readline prompt. + +Currently, there is no I/O support configured (no GPIO, ADC, etc). diff --git a/teensy/gchelper.s b/teensy/gchelper.s new file mode 100644 index 000000000..f8735d283 --- /dev/null +++ b/teensy/gchelper.s @@ -0,0 +1,36 @@ + .syntax unified + .cpu cortex-m4 + .thumb + .text + .align 2 + +@ void gc_helper_get_regs_and_clean_stack(r0=uint regs[10], r1=heap_end) + .global gc_helper_get_regs_and_clean_stack + .thumb + .thumb_func + .type gc_helper_get_regs_and_clean_stack, %function +gc_helper_get_regs_and_clean_stack: + @ store registers into given array + str r4, [r0], #4 + str r5, [r0], #4 + str r6, [r0], #4 + str r7, [r0], #4 + str r8, [r0], #4 + str r9, [r0], #4 + str r10, [r0], #4 + str r11, [r0], #4 + str r12, [r0], #4 + str r13, [r0], #4 + + @ clean the stack from given pointer up to current sp + movs r0, #0 + mov r2, sp + b.n .entry +.loop: + str r0, [r1], #4 +.entry: + cmp r1, r2 + bcc.n .loop + bx lr + + .size gc_helper_get_regs_and_clean_stack, .-gc_helper_get_regs_and_clean_stack diff --git a/teensy/led.c b/teensy/led.c new file mode 100644 index 000000000..1aad2a365 --- /dev/null +++ b/teensy/led.c @@ -0,0 +1,86 @@ +#include + +#include "misc.h" +#include "mpconfig.h" +#include "obj.h" +#include "led.h" + +#include "Arduino.h" + +void led_init(void) { +} + +void led_state(pyb_led_t led, int state) { + uint8_t pin; + + if (led == 0) { + pin = LED_BUILTIN; + } else { + return; + } + digitalWrite(pin, state); +} + +void led_toggle(pyb_led_t led) { + uint8_t pin; + + if (led == 0) { + pin = LED_BUILTIN; + } else { + return; + } + + digitalWrite(pin, !digitalRead(pin)); +} + +/******************************************************************************/ +/* Micro Python bindings */ + +typedef struct _pyb_led_obj_t { + mp_obj_base_t base; + uint led_id; +} pyb_led_obj_t; + +void led_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) { + pyb_led_obj_t *self = self_in; + print(env, "", self->led_id); +} + +mp_obj_t led_obj_on(mp_obj_t self_in) { + pyb_led_obj_t *self = self_in; + led_state(self->led_id, 1); + return mp_const_none; +} + +mp_obj_t led_obj_off(mp_obj_t self_in) { + pyb_led_obj_t *self = self_in; + led_state(self->led_id, 0); + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(led_obj_on_obj, led_obj_on); +static MP_DEFINE_CONST_FUN_OBJ_1(led_obj_off_obj, led_obj_off); + +static const mp_obj_type_t led_obj_type = { + { &mp_const_type }, + "Led", + led_obj_print, // print + NULL, // make_new + NULL, // call_n + NULL, // unary_op + NULL, // binary_op + NULL, // getiter + NULL, // iternext + { // method list + { "on", &led_obj_on_obj }, + { "off", &led_obj_off_obj }, + { NULL, NULL }, + } +}; + +mp_obj_t pyb_Led(mp_obj_t led_id) { + pyb_led_obj_t *o = m_new_obj(pyb_led_obj_t); + o->base.type = &led_obj_type; + o->led_id = mp_obj_get_int(led_id); + return o; +} diff --git a/teensy/led.h b/teensy/led.h new file mode 100644 index 000000000..c5a481254 --- /dev/null +++ b/teensy/led.h @@ -0,0 +1,9 @@ +typedef enum { + PYB_LED_BUILTIN = 0, +} pyb_led_t; + +void led_init(void); +void led_state(pyb_led_t led, int state); +void led_toggle(pyb_led_t led); + +mp_obj_t pyb_Led(mp_obj_t led_id); diff --git a/teensy/lexerteensy.c b/teensy/lexerteensy.c new file mode 100644 index 000000000..eefaf6665 --- /dev/null +++ b/teensy/lexerteensy.c @@ -0,0 +1,33 @@ +#include +#include + +#include "misc.h" +#include "lexer.h" +#include "lexerteensy.h" + +unichar str_buf_next_char(mp_lexer_str_buf_t *sb) { + if (sb->src_cur < sb->src_end) { + return *sb->src_cur++; + } else { + return MP_LEXER_CHAR_EOF; + } +} + +void str_buf_free(mp_lexer_str_buf_t *sb) { + if (sb->free) { + m_del(char, (char*)sb->src_beg, 0 /* don't know allocated size of src */); + } +} + +mp_lexer_t *mp_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str, mp_lexer_str_buf_t *sb) { + sb->free = free_str; + sb->src_beg = str; + sb->src_cur = str; + sb->src_end = str + len; + return mp_lexer_new(src_name, sb, (mp_lexer_stream_next_char_t)str_buf_next_char, (mp_lexer_stream_close_t)str_buf_free); +} + +mp_lexer_t *mp_import_open_file(qstr mod_name) { + printf("import not implemented\n"); + return NULL; +} diff --git a/teensy/lexerteensy.h b/teensy/lexerteensy.h new file mode 100644 index 000000000..961a70ada --- /dev/null +++ b/teensy/lexerteensy.h @@ -0,0 +1,8 @@ +typedef struct _py_lexer_str_buf_t { + bool free; // free src_beg when done + const char *src_beg; // beginning of source + const char *src_cur; // current location in source + const char *src_end; // end (exclusive) of source +} mp_lexer_str_buf_t; + +mp_lexer_t *mp_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str, mp_lexer_str_buf_t *sb); diff --git a/teensy/main.c b/teensy/main.c new file mode 100644 index 000000000..3711cd60d --- /dev/null +++ b/teensy/main.c @@ -0,0 +1,474 @@ +#include +#include +#include +#include + +#include "nlr.h" +#include "misc.h" +#include "mpconfig.h" +#include "mpqstr.h" +#include "lexer.h" +#include "lexerteensy.h" +#include "parse.h" +#include "obj.h" +#include "compile.h" +#include "runtime0.h" +#include "runtime.h" +#include "repl.h" +#include "usb.h" +#include "gc.h" +#include "led.h" + +#include "Arduino.h" + +extern uint32_t _heap_start; + +#ifdef USE_READLINE +#include +#include +#endif + +#if 0 +static char *str_join(const char *s1, int sep_char, const char *s2) { + int l1 = strlen(s1); + int l2 = strlen(s2); + char *s = m_new(char, l1 + l2 + 2); + memcpy(s, s1, l1); + if (sep_char != 0) { + s[l1] = sep_char; + l1 += 1; + } + memcpy(s + l1, s2, l2); + s[l1 + l2] = 0; + return s; +} + +static char *prompt(char *p) { +#ifdef USE_READLINE + char *line = readline(p); + if (line) { + add_history(line); + } +#else + static char buf[256]; + fputs(p, stdout); + char *s = fgets(buf, sizeof(buf), stdin); + if (!s) { + return NULL; + } + int l = strlen(buf); + if (buf[l - 1] == '\n') { + buf[l - 1] = 0; + } else { + l++; + } + char *line = m_new(char, l); + memcpy(line, buf, l); +#endif + return line; +} +#endif + +static const char *help_text = +"Welcome to Micro Python!\n\n" +"This is a *very* early version of Micro Python and has minimal functionality.\n\n" +"Specific commands for the board:\n" +" pyb.info() -- print some general information\n" +" pyb.gc() -- run the garbage collector\n" +" pyb.delay() -- wait for n milliseconds\n" +" pyb.Led() -- create Led object for LED n (n=0)\n" +" Led methods: on(), off()\n" +" pyb.gpio() -- read gpio pin\n" +" pyb.gpio(, ) -- set gpio pin\n" +#if 0 +" pyb.Servo() -- create Servo object for servo n (n=1,2,3,4)\n" +" Servo methods: angle()\n" +" pyb.switch() -- return True/False if switch pressed or not\n" +" pyb.accel() -- get accelerometer values\n" +" pyb.rand() -- get a 16-bit random number\n" +#endif +; + +// get some help about available functions +static mp_obj_t pyb_help(void) { + printf("%s", help_text); + return mp_const_none; +} + +// get lots of info about the board +static mp_obj_t pyb_info(void) { + // get and print unique id; 96 bits + { + byte *id = (byte*)0x40048058; + printf("ID=%02x%02x%02x%02x:%02x%02x%02x%02x:%02x%02x%02x%02x\n", id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10], id[11]); + } + + // get and print clock speeds + printf("CPU=%u\nBUS=%u\nMEM=%u\n", F_CPU, F_BUS, F_MEM); + + // to print info about memory + { + extern void *_sdata; + extern void *_edata; + extern void *_sbss; + extern void *_ebss; + extern void *_estack; + extern void *_etext; + printf("_sdata=%p\n", &_sdata); + printf("_edata=%p\n", &_edata); + printf("_sbss=%p\n", &_sbss); + printf("_ebss=%p\n", &_ebss); + printf("_estack=%p\n", &_estack); + printf("_etext=%p\n", &_etext); + printf("_heap_start=%p\n", &_heap_start); + } + + // GC info + { + gc_info_t info; + gc_info(&info); + printf("GC:\n"); + printf(" %lu total\n", info.total); + printf(" %lu used %lu free\n", info.used, info.free); + printf(" 1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block); + } + +#if 0 + // free space on flash + { + DWORD nclst; + FATFS *fatfs; + f_getfree("0:", &nclst, &fatfs); + printf("LFS free: %u bytes\n", (uint)(nclst * fatfs->csize * 512)); + } +#endif + + return mp_const_none; +} + +#define RAM_START (0x1FFF8000) // fixed for chip +#define HEAP_END (0x20006000) // tunable +#define RAM_END (0x20008000) // fixed for chip + +void gc_helper_get_regs_and_clean_stack(machine_uint_t *regs, machine_uint_t heap_end); + +void gc_collect(void) { + uint32_t start = micros(); + gc_collect_start(); + gc_collect_root((void**)RAM_START, (((uint32_t)&_heap_start) - RAM_START) / 4); + machine_uint_t regs[10]; + gc_helper_get_regs_and_clean_stack(regs, HEAP_END); + gc_collect_root((void**)HEAP_END, (RAM_END - HEAP_END) / 4); // will trace regs since they now live in this function on the stack + gc_collect_end(); + uint32_t ticks = micros() - start; // TODO implement a function that does this properly + + if (0) { + // print GC info + gc_info_t info; + gc_info(&info); + printf("GC@%lu %luus\n", start, ticks); + printf(" %lu total\n", info.total); + printf(" %lu used %lu free\n", info.used, info.free); + printf(" 1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block); + } +} + +mp_obj_t pyb_gc(void) { + gc_collect(); + return mp_const_none; +} + +mp_obj_t pyb_gpio(int n_args, mp_obj_t *args) { + //assert(1 <= n_args && n_args <= 2); + + uint pin = mp_obj_get_int(args[0]); + if (pin > CORE_NUM_DIGITAL) { + goto pin_error; + } + + if (n_args == 1) { + // get pin + pinMode(pin, INPUT); + return MP_OBJ_NEW_SMALL_INT(digitalRead(pin)); + } + + // set pin + pinMode(pin, OUTPUT); + digitalWrite(pin, rt_is_true(args[1])); + return mp_const_none; + +pin_error: + nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_ValueError, "pin %d does not exist", (void *)(machine_uint_t)pin)); +} + +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_gpio_obj, 1, 2, pyb_gpio); + +#if 0 +mp_obj_t pyb_hid_send_report(mp_obj_t arg) { + mp_obj_t *items = mp_obj_get_array_fixed_n(arg, 4); + uint8_t data[4]; + data[0] = mp_obj_get_int(items[0]); + data[1] = mp_obj_get_int(items[1]); + data[2] = mp_obj_get_int(items[2]); + data[3] = mp_obj_get_int(items[3]); + usb_hid_send_report(data); + return mp_const_none; +} +#endif + +mp_obj_t pyb_delay(mp_obj_t count) { + delay(mp_obj_get_int(count)); + return mp_const_none; +} + +mp_obj_t pyb_led(mp_obj_t state) { + led_state(PYB_LED_BUILTIN, rt_is_true(state)); + return state; +} + +char *strdup(const char *str) { + uint32_t len = strlen(str); + char *s2 = m_new(char, len + 1); + memcpy(s2, str, len); + s2[len] = 0; + return s2; +} + +#define READLINE_HIST_SIZE (8) + +static const char *readline_hist[READLINE_HIST_SIZE] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + +void stdout_tx_str(const char *str) { +// usart_tx_str(str); + usb_vcp_send_str(str); +} + +int readline(vstr_t *line, const char *prompt) { + stdout_tx_str(prompt); + int len = vstr_len(line); + int escape = 0; + int hist_num = 0; + for (;;) { + char c; + for (;;) { + if (usb_vcp_rx_any() != 0) { + c = usb_vcp_rx_get(); + break; +#if 0 + } else if (usart_rx_any()) { + c = usart_rx_char(); + break; +#endif + } + //delay(1); + //if (storage_needs_flush()) { + // storage_flush(); + //} + } + if (escape == 0) { + if (c == 4 && vstr_len(line) == len) { + return 0; + } else if (c == '\r') { + stdout_tx_str("\r\n"); + for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) { + readline_hist[i] = readline_hist[i - 1]; + } + readline_hist[0] = strdup(vstr_str(line)); + return 1; + } else if (c == 27) { + escape = true; + } else if (c == 127) { + if (vstr_len(line) > len) { + vstr_cut_tail(line, 1); + stdout_tx_str("\b \b"); + } + } else if (32 <= c && c <= 126) { + vstr_add_char(line, c); + stdout_tx_str(line->buf + line->len - 1); + } + } else if (escape == 1) { + if (c == '[') { + escape = 2; + } else { + escape = 0; + } + } else if (escape == 2) { + escape = 0; + if (c == 'A') { + // up arrow + if (hist_num < READLINE_HIST_SIZE && readline_hist[hist_num] != NULL) { + // erase line + for (int i = line->len - len; i > 0; i--) { + stdout_tx_str("\b \b"); + } + // set line to history + line->len = len; + vstr_add_str(line, readline_hist[hist_num]); + // draw line + stdout_tx_str(readline_hist[hist_num]); + // increase hist num + hist_num += 1; + } + } + } else { + escape = 0; + } + delay(10); + } +} + +void do_repl(void) { + stdout_tx_str("Micro Python for Teensy 3.1\r\n"); + stdout_tx_str("Type \"help()\" for more information.\r\n"); + + vstr_t line; + vstr_init(&line); + + for (;;) { + vstr_reset(&line); + int ret = readline(&line, ">>> "); + if (ret == 0) { + // EOF + break; + } + + if (vstr_len(&line) == 0) { + continue; + } + + if (mp_repl_is_compound_stmt(vstr_str(&line))) { + for (;;) { + vstr_add_char(&line, '\n'); + int len = vstr_len(&line); + int ret = readline(&line, "... "); + if (ret == 0 || vstr_len(&line) == len) { + // done entering compound statement + break; + } + } + } + + mp_lexer_str_buf_t sb; + mp_lexer_t *lex = mp_lexer_new_from_str_len("", vstr_str(&line), vstr_len(&line), false, &sb); + mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT); + mp_lexer_free(lex); + + if (pn != MP_PARSE_NODE_NULL) { + mp_obj_t module_fun = mp_compile(pn, true); + if (module_fun != mp_const_none) { + nlr_buf_t nlr; + uint32_t start = micros(); + if (nlr_push(&nlr) == 0) { + rt_call_function_0(module_fun); + nlr_pop(); + // optional timing + if (0) { + uint32_t ticks = micros() - start; // TODO implement a function that does this properly + printf("(took %lu us)\n", ticks); + } + } else { + // uncaught exception + mp_obj_print((mp_obj_t)nlr.ret_val); + printf("\n"); + } + } + } + } + + stdout_tx_str("\r\n"); +} + +void main(void) { + pinMode(LED_BUILTIN, OUTPUT); + // Wait for host side to get connected + while (!usb_vcp_is_connected()) { + ; + } + + led_init(); + led_state(PYB_LED_BUILTIN, 1); + +// int first_soft_reset = true; + +soft_reset: + + // GC init + gc_init(&_heap_start, (void*)HEAP_END); + + qstr_init(); + rt_init(); + +#if 1 + printf("About to add functions()\n"); + // add some functions to the python namespace + { + rt_store_name(qstr_from_str_static("help"), rt_make_function_0(pyb_help)); + mp_obj_t m = mp_obj_new_module(qstr_from_str_static("pyb")); + rt_store_attr(m, qstr_from_str_static("info"), rt_make_function_0(pyb_info)); + rt_store_attr(m, qstr_from_str_static("gc"), rt_make_function_0(pyb_gc)); + rt_store_attr(m, qstr_from_str_static("delay"), rt_make_function_1(pyb_delay)); + rt_store_attr(m, qstr_from_str_static("led"), rt_make_function_1(pyb_led)); + rt_store_attr(m, qstr_from_str_static("Led"), rt_make_function_1(pyb_Led)); + rt_store_attr(m, qstr_from_str_static("gpio"), (mp_obj_t)&pyb_gpio_obj); + rt_store_name(qstr_from_str_static("pyb"), m); + } +#endif + + // Turn bootup LED off + led_state(PYB_LED_BUILTIN, 0); + + do_repl(); + + printf("PYB: soft reboot\n"); + +// first_soft_reset = false; + goto soft_reset; +} + +double __aeabi_f2d(float x) { + // TODO + return 0.0; +} + +float __aeabi_d2f(double x) { + // TODO + return 0.0; +} + +double sqrt(double x) { + // TODO + return 0.0; +} + +machine_float_t machine_sqrt(machine_float_t x) { + // TODO + return x; +} + +// stub out __libc_init_array. It's called by mk20dx128.c and is used to call +// global C++ constructors. Since this is a C-only projects, we don't need to +// call constructors. +void __libc_init_array(void) { +} + +char * ultoa(unsigned long val, char *buf, int radix) +{ + unsigned digit; + int i=0, j; + char t; + + while (1) { + digit = val % radix; + buf[i] = ((digit < 10) ? '0' + digit : 'A' + digit - 10); + val /= radix; + if (val == 0) break; + i++; + } + buf[i + 1] = 0; + for (j=0; j < i; j++, i--) { + t = buf[j]; + buf[j] = buf[i]; + buf[i] = t; + } + return buf; +} diff --git a/teensy/main.cpp b/teensy/main.cpp new file mode 100644 index 000000000..52ce51cda --- /dev/null +++ b/teensy/main.cpp @@ -0,0 +1,245 @@ +#include +#include +#include +#include + +#include "Arduino.h" + +extern "C" +{ +#include "nlr.h" +#include "misc.h" +#include "mpconfig.h" +#include "lexer.h" +#include "lexerteensy.h" +#include "parse.h" +#include "obj.h" +#include "compile.h" +#include "runtime0.h" +#include "runtime.h" +#include "repl.h" +#include "usb.h" +} + +#ifdef USE_READLINE +#include +#include +#endif + +#if 0 +static char *str_join(const char *s1, int sep_char, const char *s2) { + int l1 = strlen(s1); + int l2 = strlen(s2); + char *s = m_new(char, l1 + l2 + 2); + memcpy(s, s1, l1); + if (sep_char != 0) { + s[l1] = sep_char; + l1 += 1; + } + memcpy(s + l1, s2, l2); + s[l1 + l2] = 0; + return s; +} + +static char *prompt(char *p) { +#ifdef USE_READLINE + char *line = readline(p); + if (line) { + add_history(line); + } +#else + static char buf[256]; + fputs(p, stdout); + char *s = fgets(buf, sizeof(buf), stdin); + if (!s) { + return NULL; + } + int l = strlen(buf); + if (buf[l - 1] == '\n') { + buf[l - 1] = 0; + } else { + l++; + } + char *line = m_new(char, l); + memcpy(line, buf, l); +#endif + return line; +} +#endif + +#define READLINE_HIST_SIZE (8) + +static const char *readline_hist[READLINE_HIST_SIZE] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + +void stdout_tx_str(const char *str) { +// usart_tx_str(str); + usb_vcp_send_str(str); +} + +static elapsedMillis ledTime; +static uint8_t ledState; + +int readline(vstr_t *line, const char *prompt) { + stdout_tx_str(prompt); + int len = vstr_len(line); + int escape = 0; + int hist_num = 0; + for (;;) { + char c; + ledState = 1; + ledTime = 0; + digitalWrite(LED_BUILTIN, ledState); + for (;;) { + if (ledTime > 200) { + ledState = !ledState; + digitalWrite(LED_BUILTIN, ledState); + ledTime = 0; + } + if (usb_vcp_rx_any() != 0) { + c = usb_vcp_rx_get(); + break; +#if 0 + } else if (usart_rx_any()) { + c = usart_rx_char(); + break; +#endif + } + //delay(1); + //if (storage_needs_flush()) { + // storage_flush(); + //} + } + if (escape == 0) { + if (c == 4 && vstr_len(line) == len) { + return 0; + } else if (c == '\r') { + stdout_tx_str("\r\n"); + for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) { + readline_hist[i] = readline_hist[i - 1]; + } + readline_hist[0] = strdup(vstr_str(line)); + return 1; + } else if (c == 27) { + escape = true; + } else if (c == 127) { + if (vstr_len(line) > len) { + vstr_cut_tail(line, 1); + stdout_tx_str("\b \b"); + } + } else if (32 <= c && c <= 126) { + vstr_add_char(line, c); + stdout_tx_str(line->buf + line->len - 1); + } + } else if (escape == 1) { + if (c == '[') { + escape = 2; + } else { + escape = 0; + } + } else if (escape == 2) { + escape = 0; + if (c == 'A') { + // up arrow + if (hist_num < READLINE_HIST_SIZE && readline_hist[hist_num] != NULL) { + // erase line + for (int i = line->len - len; i > 0; i--) { + stdout_tx_str("\b \b"); + } + // set line to history + line->len = len; + vstr_add_str(line, readline_hist[hist_num]); + // draw line + stdout_tx_str(readline_hist[hist_num]); + // increase hist num + hist_num += 1; + } + } + } else { + escape = 0; + } + delay(10); + } +} + +void setup(void) { + pinMode(LED_BUILTIN, OUTPUT); + ledState = 1; + digitalWrite(LED_BUILTIN, ledState); + ledTime = 0; + // Wait for host side to get connected + while (!usb_vcp_is_connected()) { + if (ledTime > 100) { + ledState = !ledState; + digitalWrite(LED_BUILTIN, ledState); + ledTime = 0; + } + } + digitalWrite(LED_BUILTIN, 0); + + qstr_init(); + rt_init(); + + stdout_tx_str("Micro Python for Teensy 3.1\r\n"); + stdout_tx_str("Type \"help()\" for more information.\r\n"); +} + +void loop(void) { + vstr_t line; + vstr_init(&line); + + vstr_reset(&line); + int ret = readline(&line, ">>> "); + if (ret == 0) { + // EOF + return; + } + + if (vstr_len(&line) == 0) { + return; + } + + if (mp_repl_is_compound_stmt(vstr_str(&line))) { + for (;;) { + vstr_add_char(&line, '\n'); + int len = vstr_len(&line); + int ret = readline(&line, "... "); + if (ret == 0 || vstr_len(&line) == len) { + // done entering compound statement + break; + } + } + } + + mp_lexer_str_buf_t sb; + mp_lexer_t *lex = mp_lexer_new_from_str_len("", vstr_str(&line), vstr_len(&line), false, &sb); + mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT); + mp_lexer_free(lex); + + if (pn != MP_PARSE_NODE_NULL) { + mp_obj_t module_fun = mp_compile(pn, true); + if (module_fun != mp_const_none) { + nlr_buf_t nlr; + uint32_t start = micros(); + if (nlr_push(&nlr) == 0) { + rt_call_function_0(module_fun); + nlr_pop(); + // optional timing + if (0) { + uint32_t ticks = micros() - start; // TODO implement a function that does this properly + printf("(took %lu us)\n", ticks); + } + } else { + // uncaught exception + mp_obj_print((mp_obj_t)nlr.ret_val); + printf("\r\n"); + } + } + } +} + +// for sqrt +#include +machine_float_t machine_sqrt(machine_float_t x) { + return sqrt(x); +} + diff --git a/teensy/malloc0.c b/teensy/malloc0.c new file mode 100644 index 000000000..1a6cb708e --- /dev/null +++ b/teensy/malloc0.c @@ -0,0 +1,57 @@ +#include +#include "std.h" +#include "mpconfig.h" +#include "gc.h" + +#if 0 +static uint32_t mem = 0; + +void *malloc(size_t n) { + if (mem == 0) { + extern uint32_t _heap_start; + mem = (uint32_t)&_heap_start; // need to use big ram block so we can execute code from it (is it true that we can't execute from CCM?) + } + void *ptr = (void*)mem; + mem = (mem + n + 3) & (~3); + if (mem > 0x20000000 + 0x18000) { + void __fatal_error(const char*); + __fatal_error("out of memory"); + } + return ptr; +} + +void free(void *ptr) { +} + +void *realloc(void *ptr, size_t n) { + return malloc(n); +} + +#endif + +void *calloc(size_t sz, size_t n) { + char *ptr = malloc(sz * n); + for (int i = 0; i < sz * n; i++) { + ptr[i] = 0; + } + return ptr; +} + +void *malloc(size_t n) { + void *m = gc_alloc(n); + return m; +} + +void free(void *ptr) { + gc_free(ptr); +} + +void *realloc(void *ptr, size_t n) { + return gc_realloc(ptr, n); +} + +void __assert_func(void) { + printf("\nASSERT FAIL!"); + for (;;) { + } +} diff --git a/teensy/mk20dx256.ld b/teensy/mk20dx256.ld new file mode 100644 index 000000000..da57cbe5c --- /dev/null +++ b/teensy/mk20dx256.ld @@ -0,0 +1,164 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2013 PJRC.COM, LLC. + * + * 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: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * 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. + */ + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K + RAM (rwx) : ORIGIN = 0x1FFF8000, LENGTH = 64K +} + +/* produce a link error if there is not this amount of RAM for these sections */ +_minimum_stack_size = 2K; +_minimum_heap_size = 16K; + +/* INCLUDE common.ld */ + +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2013 PJRC.COM, LLC. + * + * 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: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * 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. + */ + + + +SECTIONS +{ + .text : { + . = 0; + KEEP(*(.vectors)) + *(.startup*) + /* TODO: does linker detect startup overflow onto flashconfig? */ + . = 0x400; + KEEP(*(.flashconfig*)) + *(.text*) + *(.rodata*) + . = ALIGN(4); + KEEP(*(.init)) + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + } > FLASH = 0xFF + + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } > FLASH + _etext = .; + + .usbdescriptortable (NOLOAD) : { + /* . = ORIGIN(RAM); */ + . = ALIGN(512); + *(.usbdescriptortable*) + } > RAM + + .dmabuffers (NOLOAD) : { + . = ALIGN(4); + *(.dmabuffers*) + } > RAM + + .usbbuffers (NOLOAD) : { + . = ALIGN(4); + *(.usbbuffers*) + } > RAM + + .data : AT (_etext) { + . = ALIGN(4); + _sdata = .; + *(.data*) + . = ALIGN(4); + _edata = .; + } > RAM + + .noinit (NOLOAD) : { + *(.noinit*) + } > RAM + + .bss : { + . = ALIGN(4); + _sbss = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + __bss_end = .; + } > RAM + + /* this is to define the start of the heap, and make sure we have a minimum size */ + .heap : + { + . = ALIGN(4); + _heap_start = .; /* define a global symbol at heap start */ + . = . + _minimum_heap_size; + } >RAM + + /* this just checks there is enough RAM for the stack */ + .stack : + { + . = ALIGN(4); + . = . + _minimum_stack_size; + . = ALIGN(4); + } >RAM + + _estack = ORIGIN(RAM) + LENGTH(RAM); +} + + + + diff --git a/teensy/mpconfigport.h b/teensy/mpconfigport.h new file mode 100644 index 000000000..4cea332f3 --- /dev/null +++ b/teensy/mpconfigport.h @@ -0,0 +1,21 @@ +#include + +// options to control how Micro Python is built + +#define MICROPY_ENABLE_FLOAT (1) +#define MICROPY_EMIT_CPYTHON (0) +#define MICROPY_EMIT_X64 (0) +#define MICROPY_EMIT_THUMB (1) +#define MICROPY_EMIT_INLINE_THUMB (1) + +// type definitions for the specific machine + +#define BYTES_PER_WORD (4) + +typedef int32_t machine_int_t; // must be pointer size +typedef uint32_t machine_uint_t; // must be pointer size +typedef void *machine_ptr_t; // must be of pointer size +typedef const void *machine_const_ptr_t; // must be of pointer size +typedef float machine_float_t; + +machine_float_t machine_sqrt(machine_float_t x); diff --git a/teensy/printf.c b/teensy/printf.c new file mode 100644 index 000000000..71b27b821 --- /dev/null +++ b/teensy/printf.c @@ -0,0 +1,305 @@ +#include +#include +#include "std.h" +#include "misc.h" +//#include "lcd.h" +//#include "usart.h" +#include "usb.h" + +#define PF_FLAG_LEFT_ADJUST (0x01) +#define PF_FLAG_SHOW_SIGN (0x02) +#define PF_FLAG_SPACE_SIGN (0x04) +#define PF_FLAG_NO_TRAILZ (0x08) +#define PF_FLAG_ZERO_PAD (0x10) + +// tricky; we compute pad string by: pad_chars + (flags & PF_FLAG_ZERO_PAD) +#define PF_PAD_SIZE PF_FLAG_ZERO_PAD +static const char *pad_chars = " 0000000000000000"; + +typedef struct _pfenv_t { + void *data; + void (*print_strn)(void *, const char *str, unsigned int len); +} pfenv_t; + +static void print_str_dummy(void *data, const char *str, unsigned int len) { +} + +const pfenv_t pfenv_dummy = {0, print_str_dummy}; + +static int pfenv_print_strn(const pfenv_t *pfenv, const char *str, unsigned int len, int flags, int width) { + int pad = width - len; + if (pad > 0 && (flags & PF_FLAG_LEFT_ADJUST) == 0) { + while (pad > 0) { + int p = pad; + if (p > PF_PAD_SIZE) + p = PF_PAD_SIZE; + pfenv->print_strn(pfenv->data, pad_chars + (flags & PF_FLAG_ZERO_PAD), p); + pad -= p; + } + } + pfenv->print_strn(pfenv->data, str, len); + while (pad > 0) { + int p = pad; + if (p > PF_PAD_SIZE) + p = PF_PAD_SIZE; + pfenv->print_strn(pfenv->data, pad_chars, p); + pad -= p; + } + return len; +} + +// enough room for 32 signed number +#define INT_BUF_SIZE (12) + +static int pfenv_print_int(const pfenv_t *pfenv, unsigned int x, int sgn, int base, int base_char, int flags, int width) { + char sign = 0; + if (sgn) { + if ((int)x < 0) { + sign = '-'; + x = -x; + } else if (flags & PF_FLAG_SHOW_SIGN) { + sign = '+'; + } else if (flags & PF_FLAG_SPACE_SIGN) { + sign = ' '; + } + } + + char buf[INT_BUF_SIZE]; + char *b = buf + INT_BUF_SIZE; + + if (x == 0) { + *(--b) = '0'; + } else { + do { + int c = x % base; + x /= base; + if (c >= 10) { + c += base_char - 10; + } else { + c += '0'; + } + *(--b) = c; + } while (b > buf && x != 0); + } + + if (b > buf && sign != 0) { + *(--b) = sign; + } + + return pfenv_print_strn(pfenv, b, buf + INT_BUF_SIZE - b, flags, width); +} + +void pfenv_prints(const pfenv_t *pfenv, const char *str) { + pfenv->print_strn(pfenv->data, str, strlen(str)); +} + +int pfenv_printf(const pfenv_t *pfenv, const char *fmt, va_list args) { + int chrs = 0; + for (;;) { + { + const char *f = fmt; + while (*f != '\0' && *f != '%') { + ++f; // XXX UTF8 advance char + } + if (f > fmt) { + pfenv->print_strn(pfenv->data, fmt, f - fmt); + chrs += f - fmt; + fmt = f; + } + } + + if (*fmt == '\0') { + break; + } + + // move past % character + ++fmt; + + // parse flags, if they exist + int flags = 0; + while (*fmt != '\0') { + if (*fmt == '-') flags |= PF_FLAG_LEFT_ADJUST; + else if (*fmt == '+') flags |= PF_FLAG_SHOW_SIGN; + else if (*fmt == ' ') flags |= PF_FLAG_SPACE_SIGN; + else if (*fmt == '!') flags |= PF_FLAG_NO_TRAILZ; + else if (*fmt == '0') flags |= PF_FLAG_ZERO_PAD; + else break; + ++fmt; + } + + // parse width, if it exists + int width = 0; + for (; '0' <= *fmt && *fmt <= '9'; ++fmt) { + width = width * 10 + *fmt - '0'; + } + + // parse precision, if it exists + int prec = -1; + if (*fmt == '.') { + ++fmt; + if (*fmt == '*') { + ++fmt; + prec = va_arg(args, int); + } else { + prec = 0; + for (; '0' <= *fmt && *fmt <= '9'; ++fmt) { + prec = prec * 10 + *fmt - '0'; + } + } + if (prec < 0) { + prec = 0; + } + } + + // parse long specifiers (current not used) + //bool long_arg = false; + if (*fmt == 'l') { + ++fmt; + //long_arg = true; + } + + if (*fmt == '\0') { + break; + } + + switch (*fmt) { + case 'b': + if (va_arg(args, int)) { + chrs += pfenv_print_strn(pfenv, "true", 4, flags, width); + } else { + chrs += pfenv_print_strn(pfenv, "false", 5, flags, width); + } + break; + case 'c': + { + char str = va_arg(args, int); + chrs += pfenv_print_strn(pfenv, &str, 1, flags, width); + break; + } + case 's': + { + const char *str = va_arg(args, const char*); + if (str) { + if (prec < 0) { + prec = strlen(str); + } + chrs += pfenv_print_strn(pfenv, str, prec, flags, width); + } else { + chrs += pfenv_print_strn(pfenv, "(null)", 6, flags, width); + } + break; + } + case 'u': + chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 10, 'a', flags, width); + break; + case 'd': + chrs += pfenv_print_int(pfenv, va_arg(args, int), 1, 10, 'a', flags, width); + break; + case 'x': + case 'p': // ? + chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'a', flags, width); + break; + case 'X': + case 'P': // ? + chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'A', flags, width); + break; + default: + pfenv->print_strn(pfenv->data, fmt, 1); + chrs += 1; + break; + } + ++fmt; + } + return chrs; +} + +void stdout_print_strn(void *data, const char *str, unsigned int len) { + // send stdout to USART, USB CDC VCP, and LCD if nothing else +#if 0 + int any = 0; + if (usart_is_enabled()) { + usart_tx_strn_cooked(str, len); + any = true; + } +#endif + if (usb_vcp_is_enabled()) { + usb_vcp_send_strn_cooked(str, len); +// any = true; + } +#if 0 + if (!any) { + lcd_print_strn(str, len); + } +#endif +} + +static const pfenv_t pfenv_stdout = {0, stdout_print_strn}; + +int printf(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int ret = pfenv_printf(&pfenv_stdout, fmt, ap); + va_end(ap); + return ret; +} + +int vprintf(const char *fmt, va_list ap) { + return pfenv_printf(&pfenv_stdout, fmt, ap); +} + +// need this because gcc optimises printf("%c", c) -> putchar(c), and printf("a") -> putchar('a') +int putchar(int c) { + char chr = c; + stdout_print_strn(0, &chr, 1); + return chr; +} + +// need this because gcc optimises printf("string\n") -> puts("string") +int puts(const char *s) { + stdout_print_strn(0, s, strlen(s)); + char chr = '\n'; + stdout_print_strn(0, &chr, 1); + return 1; +} + +typedef struct _strn_pfenv_t { + char *cur; + size_t remain; +} strn_pfenv_t; + +void strn_print_strn(void *data, const char *str, unsigned int len) { + strn_pfenv_t *strn_pfenv = data; + if (len > strn_pfenv->remain) { + len = strn_pfenv->remain; + } + memcpy(strn_pfenv->cur, str, len); + strn_pfenv->cur += len; + strn_pfenv->remain -= len; +} + +int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { + strn_pfenv_t strn_pfenv; + strn_pfenv.cur = str; + strn_pfenv.remain = size; + pfenv_t pfenv; + pfenv.data = &strn_pfenv; + pfenv.print_strn = strn_print_strn; + int len = pfenv_printf(&pfenv, fmt, ap); + // add terminating null byte + if (size > 0) { + if (strn_pfenv.remain == 0) { + strn_pfenv.cur[-1] = 0; + } else { + strn_pfenv.cur[0] = 0; + } + } + return len; +} + +int snprintf(char *str, size_t size, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int ret = vsnprintf(str, size, fmt, ap); + va_end(ap); + return ret; +} diff --git a/teensy/std.h b/teensy/std.h new file mode 100644 index 000000000..587b9f888 --- /dev/null +++ b/teensy/std.h @@ -0,0 +1,22 @@ +typedef unsigned int size_t; + +void __assert_func(void); + +void *malloc(size_t n); +void free(void *ptr); +void *calloc(size_t sz, size_t n); +void *realloc(void *ptr, size_t n); + +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); + +int strlen(const char *str); +int strcmp(const char *s1, const char *s2); +int strncmp(const char *s1, const char *s2, size_t n); +char *strndup(const char *s, size_t n); +char *strcpy(char *dest, const char *src); +char *strcat(char *dest, const char *src); + +int printf(const char *fmt, ...); +int snprintf(char *str, size_t size, const char *fmt, ...); diff --git a/teensy/string0.c b/teensy/string0.c new file mode 100644 index 000000000..2a5f25597 --- /dev/null +++ b/teensy/string0.c @@ -0,0 +1,110 @@ +#include +#include "std.h" + +void *memcpy(void *dest, const void *src, size_t n) { + // TODO align and copy 32 bits at a time + uint8_t *d = dest; + const uint8_t *s = src; + for (; n > 0; n--) { + *d++ = *s++; + } + return dest; +} + +void *memmove(void *dest, const void *src, size_t n) { + if (src < dest && dest < src + n) { + // need to copy backwards + uint8_t *d = dest + n - 1; + const uint8_t *s = src + n - 1; + for (; n > 0; n--) { + *d-- = *s--; + } + return dest; + } else { + // can use normal memcpy + return memcpy(dest, src, n); + } +} + +void *memset(void *s, int c, size_t n) { + uint8_t *s2 = s; + for (; n > 0; n--) { + *s2++ = c; + } + return s; +} + +int strlen(const char *str) { + int len = 0; + for (const char *s = str; *s; s++) { + len += 1; + } + return len; +} + +int strcmp(const char *s1, const char *s2) { + while (*s1 && *s2) { + char c1 = *s1++; // XXX UTF8 get char, next char + char c2 = *s2++; // XXX UTF8 get char, next char + if (c1 < c2) return -1; + else if (c1 > c2) return 1; + } + if (*s2) return -1; + else if (*s1) return 1; + else return 0; +} + +int strncmp(const char *s1, const char *s2, size_t n) { + while (*s1 && *s2 && n > 0) { + char c1 = *s1++; // XXX UTF8 get char, next char + char c2 = *s2++; // XXX UTF8 get char, next char + n--; + if (c1 < c2) return -1; + else if (c1 > c2) return 1; + } + if (n == 0) return 0; + else if (*s2) return -1; + else if (*s1) return 1; + else return 0; +} + +char *strndup(const char *s, size_t n) { + size_t len = strlen(s); + if (n > len) { + n = len; + } + char *s2 = malloc(n + 1); + memcpy(s2, s, n); + s2[n] = '\0'; + return s2; +} + +char *strcpy(char *dest, const char *src) { + char *d = dest; + while (*src) { + *d++ = *src++; + } + *d = '\0'; + return dest; +} + +// needed because gcc optimises strcpy + strcat to this +char *stpcpy(char *dest, const char *src) { + while (*src) { + *dest++ = *src++; + } + *dest = '\0'; + return dest; +} + +char *strcat(char *dest, const char *src) { + char *d = dest; + while (*d) { + d++; + } + while (*src) { + *d++ = *src++; + } + *d = '\0'; + return dest; +} diff --git a/teensy/usb.c b/teensy/usb.c new file mode 100644 index 000000000..a045a2ed6 --- /dev/null +++ b/teensy/usb.c @@ -0,0 +1,44 @@ +#include "Arduino.h" + +#include "usb.h" +#include "usb_serial.h" + +int usb_vcp_is_connected(void) +{ + return usb_configuration && (usb_cdc_line_rtsdtr & (USB_SERIAL_DTR | USB_SERIAL_RTS)); +} + +int usb_vcp_is_enabled(void) +{ + return 1; +} + +int usb_vcp_rx_any(void) +{ + return usb_serial_available(); +} + +char usb_vcp_rx_get(void) +{ + return usb_serial_getchar(); +} + +void usb_vcp_send_str(const char* str) +{ + usb_vcp_send_strn(str, strlen(str)); +} + +void usb_vcp_send_strn(const char* str, int len) +{ + usb_serial_write(str, len); +} + +void usb_vcp_send_strn_cooked(const char *str, int len) +{ + for (const char *top = str + len; str < top; str++) { + if (*str == '\n') { + usb_serial_putchar('\r'); + } + usb_serial_putchar(*str); + } +} diff --git a/teensy/usb.h b/teensy/usb.h new file mode 100644 index 000000000..8f32309ec --- /dev/null +++ b/teensy/usb.h @@ -0,0 +1,9 @@ +void usb_init(void); +int usb_vcp_is_enabled(void); +int usb_vcp_is_connected(void); +int usb_vcp_rx_any(void); +char usb_vcp_rx_get(void); +void usb_vcp_send_str(const char* str); +void usb_vcp_send_strn(const char* str, int len); +void usb_vcp_send_strn_cooked(const char *str, int len); +void usb_hid_send_report(uint8_t *buf); // 4 bytes for mouse: ?, x, y, ? From af61a1a4923924b9868d488e2e76126a9fc14069 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 6 Jan 2014 17:49:46 +0200 Subject: [PATCH 03/18] Use constructor to create small int (avoid exposing mp_obj_t internals to VM). --- py/vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/vm.c b/py/vm.c index 8e7ef7485..5e3ec0baf 100644 --- a/py/vm.c +++ b/py/vm.c @@ -106,7 +106,7 @@ bool mp_execute_byte_code_2(const byte **ip_in_out, mp_obj_t *fastn, mp_obj_t ** case MP_BC_LOAD_CONST_SMALL_INT: unum = (ip[0] | (ip[1] << 8) | (ip[2] << 16)) - 0x800000; ip += 3; - PUSH((mp_obj_t)(unum << 1 | 1)); + PUSH(MP_OBJ_NEW_SMALL_INT(unum)); break; case MP_BC_LOAD_CONST_DEC: From 24507aff7d8a4580ac35d87d8fc762858691c88c Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 6 Jan 2014 17:49:21 +0200 Subject: [PATCH 04/18] Typo fix in comment. --- py/emit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/emit.h b/py/emit.h index ea6573103..66e87fd4c 100644 --- a/py/emit.h +++ b/py/emit.h @@ -5,7 +5,7 @@ * is not known until the end of pass 1. * As a consequence, we don't know the maximum stack size until the end of pass 2. * This is problematic for some emitters (x64) since they need to know the maximum - * stack size to compile the entry to the function, and this effects code size. + * stack size to compile the entry to the function, and this affects code size. */ typedef enum { From a41fe31322e92f84ecee4f3e6295bed2ec9120fd Mon Sep 17 00:00:00 2001 From: "John R. Lenton" Date: Mon, 6 Jan 2014 17:17:02 +0000 Subject: [PATCH 05/18] Added dict iterator. --- py/objdict.c | 83 +++++++++++++++++++++++++---- tests/basics/tests/dict_iterator.py | 3 ++ 2 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 tests/basics/tests/dict_iterator.py diff --git a/py/objdict.c b/py/objdict.c index b3e21aedd..4cd636379 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -17,20 +17,23 @@ typedef struct _mp_obj_dict_t { mp_map_t map; } mp_obj_dict_t; +static mp_obj_t mp_obj_new_dict_iterator(mp_obj_dict_t *dict, int cur); +static mp_map_elem_t *dict_it_iternext_elem(mp_obj_t self_in); + static void dict_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) { mp_obj_dict_t *self = self_in; bool first = true; print(env, "{"); - for (int i = 0; i < self->map.alloc; i++) { - if (self->map.table[i].key != NULL) { - if (!first) { - print(env, ", "); - } - first = false; - mp_obj_print_helper(print, env, self->map.table[i].key); - print(env, ": "); - mp_obj_print_helper(print, env, self->map.table[i].value); + mp_obj_t *dict_iter = mp_obj_new_dict_iterator(self, 0); + mp_map_elem_t *next = NULL; + while ((next = dict_it_iternext_elem(dict_iter)) != NULL) { + if (!first) { + print(env, ", "); } + first = false; + mp_obj_print_helper(print, env, next->key); + print(env, ": "); + mp_obj_print_helper(print, env, next->value); } print(env, "}"); } @@ -60,13 +63,73 @@ static mp_obj_t dict_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { } } + +/******************************************************************************/ +/* dict iterator */ + +typedef struct _mp_obj_dict_it_t { + mp_obj_base_t base; + mp_obj_dict_t *dict; + machine_uint_t cur; +} mp_obj_dict_it_t; + +static mp_map_elem_t *dict_it_iternext_elem(mp_obj_t self_in) { + mp_obj_dict_it_t *self = self_in; + machine_uint_t max = self->dict->map.alloc; + mp_map_elem_t *table = self->dict->map.table; + + for (int i = self->cur; i < max; i++) { + if (table[i].key != NULL) { + self->cur = i + 1; + return &(table[i]); + } + } + + return NULL; +} + +mp_obj_t dict_it_iternext(mp_obj_t self_in) { + mp_map_elem_t *next = dict_it_iternext_elem(self_in); + + if (next != NULL) { + return next->key; + } else { + return mp_const_stop_iteration; + } +} + +static const mp_obj_type_t dict_it_type = { + { &mp_const_type }, + "dict_iterator", + .iternext = dict_it_iternext, + .methods = { { NULL, NULL }, }, +}; + +static mp_obj_t mp_obj_new_dict_iterator(mp_obj_dict_t *dict, int cur) { + mp_obj_dict_it_t *o = m_new_obj(mp_obj_dict_it_t); + o->base.type = &dict_it_type; + o->dict = dict; + o->cur = cur; + return o; +} + +static mp_obj_t dict_getiter(mp_obj_t o_in) { + return mp_obj_new_dict_iterator(o_in, 0); +} + +/******************************************************************************/ +/* dict methods */ + +/******************************************************************************/ +/* dict constructors & etc */ + const mp_obj_type_t dict_type = { { &mp_const_type }, "dict", .print = dict_print, .make_new = dict_make_new, .binary_op = dict_binary_op, - .getiter = NULL, + .getiter = dict_getiter, .methods = {{NULL, NULL},}, }; diff --git a/tests/basics/tests/dict_iterator.py b/tests/basics/tests/dict_iterator.py new file mode 100644 index 000000000..f190e32ff --- /dev/null +++ b/tests/basics/tests/dict_iterator.py @@ -0,0 +1,3 @@ +d = {1: 2, 3: 4} +for i in d: + print(i, d[i]) From 4ce6ceadcad294a62f6fb2b23da262dc5cad0793 Mon Sep 17 00:00:00 2001 From: "John R. Lenton" Date: Mon, 6 Jan 2014 17:38:47 +0000 Subject: [PATCH 06/18] Added dict.clear. Added 0 to the list of primes. Funky primes, these. --- py/map.c | 48 ++++++++++++++++++++++++-------- py/map.h | 1 + py/objdict.c | 16 ++++++++++- tests/basics/tests/dict_clear.py | 6 ++++ 4 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 tests/basics/tests/dict_clear.py diff --git a/py/map.c b/py/map.c index 01209c9b7..dff0bdfea 100644 --- a/py/map.c +++ b/py/map.c @@ -9,7 +9,8 @@ #include "map.h" // approximatelly doubling primes; made with Mathematica command: Table[Prime[Floor[(1.7)^n]], {n, 3, 24}] -static int doubling_primes[] = {7, 19, 43, 89, 179, 347, 647, 1229, 2297, 4243, 7829, 14347, 26017, 47149, 84947, 152443, 273253, 488399, 869927, 1547173, 2745121, 4861607}; +// prefixed with zero for the empty case. +static int doubling_primes[] = {0, 7, 19, 43, 89, 179, 347, 647, 1229, 2297, 4243, 7829, 14347, 26017, 47149, 84947, 152443, 273253, 488399, 869927, 1547173, 2745121, 4861607}; int get_doubling_prime_greater_or_equal_to(int x) { for (int i = 0; i < sizeof(doubling_primes) / sizeof(int); i++) { @@ -38,6 +39,31 @@ mp_map_t *mp_map_new(mp_map_kind_t kind, int n) { return map; } +void mp_map_clear(mp_map_t *map) { + map->used = 0; + machine_uint_t a = map->alloc; + map->alloc = 0; + map->table = m_renew(mp_map_elem_t, map->table, a, map->alloc); + mp_map_elem_t nul = {NULL, NULL}; + for (uint i=0; ialloc; i++) { + map->table[i] = nul; + } +} + +static void mp_map_rehash (mp_map_t *map) { + int old_alloc = map->alloc; + mp_map_elem_t *old_table = map->table; + map->alloc = get_doubling_prime_greater_or_equal_to(map->alloc + 1); + map->used = 0; + map->table = m_new0(mp_map_elem_t, map->alloc); + for (int i = 0; i < old_alloc; i++) { + if (old_table[i].key != NULL) { + mp_map_lookup_helper(map, old_table[i].key, true)->value = old_table[i].value; + } + } + m_del(mp_map_elem_t, old_table, old_alloc); +} + mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found) { bool is_map_mp_obj = (map->kind == MP_MAP_OBJ); machine_uint_t hash; @@ -46,6 +72,13 @@ mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_n } else { hash = (machine_uint_t)index; } + if (map->alloc == 0) { + if (add_if_not_found) { + mp_map_rehash(map); + } else { + return NULL; + } + } uint pos = hash % map->alloc; for (;;) { mp_map_elem_t *elem = &map->table[pos]; @@ -54,17 +87,7 @@ mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_n if (add_if_not_found) { if (map->used + 1 >= map->alloc) { // not enough room in table, rehash it - int old_alloc = map->alloc; - mp_map_elem_t *old_table = map->table; - map->alloc = get_doubling_prime_greater_or_equal_to(map->alloc + 1); - map->used = 0; - map->table = m_new0(mp_map_elem_t, map->alloc); - for (int i = 0; i < old_alloc; i++) { - if (old_table[i].key != NULL) { - mp_map_lookup_helper(map, old_table[i].key, true)->value = old_table[i].value; - } - } - m_del(mp_map_elem_t, old_table, old_alloc); + mp_map_rehash(map); // restart the search for the new element pos = hash % map->alloc; } else { @@ -106,6 +129,7 @@ void mp_set_init(mp_set_t *set, int n) { mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, bool add_if_not_found) { int hash = mp_obj_hash(index); + assert(set->alloc); /* FIXME: if alloc is ever 0 when doing a lookup, this'll fail: */ int pos = hash % set->alloc; for (;;) { mp_obj_t elem = set->table[pos]; diff --git a/py/map.h b/py/map.h index f8ca886aa..b4592c270 100644 --- a/py/map.h +++ b/py/map.h @@ -28,6 +28,7 @@ void mp_map_init(mp_map_t *map, mp_map_kind_t kind, int n); mp_map_t *mp_map_new(mp_map_kind_t kind, int n); mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found); mp_map_elem_t* mp_qstr_map_lookup(mp_map_t *map, qstr index, bool add_if_not_found); +void mp_map_clear(mp_map_t *map); void mp_set_init(mp_set_t *set, int n); mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, bool add_if_not_found); diff --git a/py/objdict.c b/py/objdict.c index 4cd636379..1a5e162e5 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -120,9 +120,20 @@ static mp_obj_t dict_getiter(mp_obj_t o_in) { /******************************************************************************/ /* dict methods */ +static mp_obj_t dict_clear(mp_obj_t self_in) { + assert(MP_OBJ_IS_TYPE(self_in, &dict_type)); + mp_obj_dict_t *self = self_in; + + mp_map_clear(&self->map); + + return mp_const_none; +} + /******************************************************************************/ /* dict constructors & etc */ +static MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear); + const mp_obj_type_t dict_type = { { &mp_const_type }, "dict", @@ -130,7 +141,10 @@ const mp_obj_type_t dict_type = { .make_new = dict_make_new, .binary_op = dict_binary_op, .getiter = dict_getiter, - .methods = {{NULL, NULL},}, + .methods = { + { "clear", &dict_clear_obj }, + { NULL, NULL }, // end-of-list sentinel + }, }; mp_obj_t mp_obj_new_dict(int n_args) { diff --git a/tests/basics/tests/dict_clear.py b/tests/basics/tests/dict_clear.py new file mode 100644 index 000000000..6be2778be --- /dev/null +++ b/tests/basics/tests/dict_clear.py @@ -0,0 +1,6 @@ +d = {1: 2, 3: 4} +print(d) +d.clear() +print(d) +d[2] = 42 +print(d) From 7d21d516d2eb77242c8ade4ae83f0d438e86f072 Mon Sep 17 00:00:00 2001 From: "John R. Lenton" Date: Mon, 6 Jan 2014 17:54:04 +0000 Subject: [PATCH 07/18] make dict_len use the map's used count --- py/objdict.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/py/objdict.c b/py/objdict.c index 1a5e162e5..951e1ec06 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -128,12 +128,12 @@ static mp_obj_t dict_clear(mp_obj_t self_in) { return mp_const_none; } +static MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear); + /******************************************************************************/ /* dict constructors & etc */ -static MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear); - const mp_obj_type_t dict_type = { { &mp_const_type }, "dict", @@ -156,13 +156,7 @@ mp_obj_t mp_obj_new_dict(int n_args) { uint mp_obj_dict_len(mp_obj_t self_in) { mp_obj_dict_t *self = self_in; - uint len = 0; - for (int i = 0; i < self->map.alloc; i++) { - if (self->map.table[i].key != NULL) { - len += 1; - } - } - return len; + return self->map.used; } mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) { From d90b19eca56a361d432ab6af9c2305bb3f708e1c Mon Sep 17 00:00:00 2001 From: "John R. Lenton" Date: Mon, 6 Jan 2014 18:11:20 +0000 Subject: [PATCH 08/18] Added dict.copy --- py/objdict.c | 10 ++++++++++ tests/basics/tests/dict_copy.py | 5 +++++ 2 files changed, 15 insertions(+) create mode 100644 tests/basics/tests/dict_copy.py diff --git a/py/objdict.c b/py/objdict.c index 951e1ec06..7567a612c 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -130,6 +130,15 @@ static mp_obj_t dict_clear(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear); +static mp_obj_t dict_copy(mp_obj_t self_in) { + assert(MP_OBJ_IS_TYPE(self_in, &dict_type)); + mp_obj_dict_t *self = self_in; + mp_obj_dict_t *other = mp_obj_new_dict(self->map.alloc); + other->map.used = self->map.used; + memcpy(other->map.table, self->map.table, self->map.alloc * sizeof(mp_map_elem_t)); + return other; +} +static MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy); /******************************************************************************/ /* dict constructors & etc */ @@ -143,6 +152,7 @@ const mp_obj_type_t dict_type = { .getiter = dict_getiter, .methods = { { "clear", &dict_clear_obj }, + { "copy", &dict_copy_obj }, { NULL, NULL }, // end-of-list sentinel }, }; diff --git a/tests/basics/tests/dict_copy.py b/tests/basics/tests/dict_copy.py new file mode 100644 index 000000000..c3eb7ffc1 --- /dev/null +++ b/tests/basics/tests/dict_copy.py @@ -0,0 +1,5 @@ +a = {i: 2*i for i in range(1000)} +b = a.copy() +for i in range(1000): + print(i, b[i]) +print(len(b)) From cd0887352d18467ea96728e75255ea36f425450d Mon Sep 17 00:00:00 2001 From: "John R. Lenton" Date: Mon, 6 Jan 2014 18:53:16 +0000 Subject: [PATCH 09/18] Added dict.get. --- py/objdict.c | 18 ++++++++++++++++-- tests/basics/tests/dict_get.py | 3 +++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 tests/basics/tests/dict_get.py diff --git a/py/objdict.c b/py/objdict.c index 7567a612c..fd5e37dfe 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -140,6 +140,20 @@ static mp_obj_t dict_copy(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy); +static mp_obj_t dict_get(int n_args, const mp_obj_t *args) { + assert(2 <= n_args && n_args <= 3); + assert(MP_OBJ_IS_TYPE(args[0], &dict_type)); + + mp_map_elem_t *elem = mp_map_lookup_helper(&((mp_obj_dict_t *)args[0])->map, + args[1], false); + if (elem == NULL) { + return n_args >= 3 ? args[2] : mp_const_none; + } else { + return elem->value; + } +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_get_obj, 2, 3, dict_get); + /******************************************************************************/ /* dict constructors & etc */ @@ -153,6 +167,7 @@ const mp_obj_type_t dict_type = { .methods = { { "clear", &dict_clear_obj }, { "copy", &dict_copy_obj }, + { "get", &dict_get_obj }, { NULL, NULL }, // end-of-list sentinel }, }; @@ -165,8 +180,7 @@ mp_obj_t mp_obj_new_dict(int n_args) { } uint mp_obj_dict_len(mp_obj_t self_in) { - mp_obj_dict_t *self = self_in; - return self->map.used; + return ((mp_obj_dict_t *)self_in)->map.used; } mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) { diff --git a/tests/basics/tests/dict_get.py b/tests/basics/tests/dict_get.py new file mode 100644 index 000000000..fb43a45ea --- /dev/null +++ b/tests/basics/tests/dict_get.py @@ -0,0 +1,3 @@ +for d in {}, {42:2}: + print(d.get(42)) + print(d.get(42,2)) From 0fcbaa442f538c8ffca8a7387f7dc6d280172a5c Mon Sep 17 00:00:00 2001 From: "John R. Lenton" Date: Mon, 6 Jan 2014 19:48:34 +0000 Subject: [PATCH 10/18] implemented dict.pop --- py/map.c | 15 ++++++++-- py/map.h | 2 +- py/objdict.c | 51 ++++++++++++++++++++++++++++------ tests/basics/tests/dict_pop.py | 12 ++++++++ 4 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 tests/basics/tests/dict_pop.py diff --git a/py/map.c b/py/map.c index dff0bdfea..8554150ad 100644 --- a/py/map.c +++ b/py/map.c @@ -58,13 +58,13 @@ static void mp_map_rehash (mp_map_t *map) { map->table = m_new0(mp_map_elem_t, map->alloc); for (int i = 0; i < old_alloc; i++) { if (old_table[i].key != NULL) { - mp_map_lookup_helper(map, old_table[i].key, true)->value = old_table[i].value; + mp_map_lookup_helper(map, old_table[i].key, true, false)->value = old_table[i].value; } } m_del(mp_map_elem_t, old_table, old_alloc); } -mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found) { +mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found, bool remove_if_found) { bool is_map_mp_obj = (map->kind == MP_MAP_OBJ); machine_uint_t hash; if (is_map_mp_obj) { @@ -105,6 +105,15 @@ mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_n elem->key = index; } */ + if (remove_if_found) { + map->used--; + /* this leaks this memory (but see dict_get_helper) */ + mp_map_elem_t *retval = m_new(mp_map_elem_t, 1); + retval->key = elem->key; + retval->value = elem->value; + elem->key = NULL; + return retval; + } return elem; } else { // not yet found, keep searching in this table @@ -115,7 +124,7 @@ mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_n mp_map_elem_t* mp_qstr_map_lookup(mp_map_t *map, qstr index, bool add_if_not_found) { mp_obj_t o = (mp_obj_t)(machine_uint_t)index; - return mp_map_lookup_helper(map, o, add_if_not_found); + return mp_map_lookup_helper(map, o, add_if_not_found, false); } /******************************************************************************/ diff --git a/py/map.h b/py/map.h index b4592c270..f0a57758c 100644 --- a/py/map.h +++ b/py/map.h @@ -26,7 +26,7 @@ typedef struct _mp_set_t { int get_doubling_prime_greater_or_equal_to(int x); void mp_map_init(mp_map_t *map, mp_map_kind_t kind, int n); mp_map_t *mp_map_new(mp_map_kind_t kind, int n); -mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found); +mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found, bool remove_if_found); mp_map_elem_t* mp_qstr_map_lookup(mp_map_t *map, qstr index, bool add_if_not_found); void mp_map_clear(mp_map_t *map); diff --git a/py/objdict.c b/py/objdict.c index fd5e37dfe..935829ddb 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -50,7 +50,7 @@ static mp_obj_t dict_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { case RT_BINARY_OP_SUBSCR: { // dict load - mp_map_elem_t *elem = mp_map_lookup_helper(&o->map, rhs_in, false); + mp_map_elem_t *elem = mp_map_lookup_helper(&o->map, rhs_in, false, false); if (elem == NULL) { nlr_jump(mp_obj_new_exception_msg(MP_QSTR_KeyError, "")); } else { @@ -140,20 +140,52 @@ static mp_obj_t dict_copy(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy); +static mp_obj_t dict_get_helper(mp_map_t *self, mp_obj_t key, mp_obj_t deflt, bool pop) { + mp_map_elem_t *elem = mp_map_lookup_helper(self, key, false, pop); + if (elem == NULL) { + if (deflt == NULL) { + if (pop) { + nlr_jump(mp_obj_new_exception_msg(MP_QSTR_KeyError, "")); + } else { + return mp_const_none; + } + } else { + return deflt; + } + } else { + mp_obj_t value = elem->value; + if (pop) { + /* catch the leak (from mp_map_lookup_helper) */ + m_free(elem, 2 * sizeof(mp_obj_t)); + } + return value; + } + +} + static mp_obj_t dict_get(int n_args, const mp_obj_t *args) { assert(2 <= n_args && n_args <= 3); assert(MP_OBJ_IS_TYPE(args[0], &dict_type)); - mp_map_elem_t *elem = mp_map_lookup_helper(&((mp_obj_dict_t *)args[0])->map, - args[1], false); - if (elem == NULL) { - return n_args >= 3 ? args[2] : mp_const_none; - } else { - return elem->value; - } + return dict_get_helper(&((mp_obj_dict_t *)args[0])->map, + args[1], + n_args == 3 ? args[2] : NULL, + false); } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_get_obj, 2, 3, dict_get); +static mp_obj_t dict_pop(int n_args, const mp_obj_t *args) { + assert(2 <= n_args && n_args <= 3); + assert(MP_OBJ_IS_TYPE(args[0], &dict_type)); + + return dict_get_helper(&((mp_obj_dict_t *)args[0])->map, + args[1], + n_args == 3 ? args[2] : NULL, + true); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_pop_obj, 2, 3, dict_pop); + + /******************************************************************************/ /* dict constructors & etc */ @@ -168,6 +200,7 @@ const mp_obj_type_t dict_type = { { "clear", &dict_clear_obj }, { "copy", &dict_copy_obj }, { "get", &dict_get_obj }, + { "pop", &dict_pop_obj }, { NULL, NULL }, // end-of-list sentinel }, }; @@ -186,6 +219,6 @@ uint mp_obj_dict_len(mp_obj_t self_in) { mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) { assert(MP_OBJ_IS_TYPE(self_in, &dict_type)); mp_obj_dict_t *self = self_in; - mp_map_lookup_helper(&self->map, key, true)->value = value; + mp_map_lookup_helper(&self->map, key, true, false)->value = value; return self_in; } diff --git a/tests/basics/tests/dict_pop.py b/tests/basics/tests/dict_pop.py new file mode 100644 index 000000000..602560ce9 --- /dev/null +++ b/tests/basics/tests/dict_pop.py @@ -0,0 +1,12 @@ +d = {1: 2, 3: 4} +print(d.pop(3), d) +print(d) +print(d.pop(1, 42), d) +print(d.pop(1, 42), d) +print(d.pop(1, None), d) +try: + print(d.pop(1), "!!!",) +except KeyError: + print("Raised KeyError") +else: + print("Did not rise KeyError!") From f77dce8a5dda13c759faeabb892d5f439dc05fd5 Mon Sep 17 00:00:00 2001 From: "John R. Lenton" Date: Mon, 6 Jan 2014 20:08:52 +0000 Subject: [PATCH 11/18] Added dict.popitem --- py/objdict.c | 21 +++++++++++++++++++++ tests/basics/tests/dict_popitem.py | 11 +++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/basics/tests/dict_popitem.py diff --git a/py/objdict.c b/py/objdict.c index 935829ddb..b4cf8f4aa 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -186,6 +186,26 @@ static mp_obj_t dict_pop(int n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_pop_obj, 2, 3, dict_pop); + +static mp_obj_t dict_popitem(mp_obj_t self_in) { + assert(MP_OBJ_IS_TYPE(self_in, &dict_type)); + mp_obj_dict_t *self = self_in; + if (self->map.used == 0) { + nlr_jump(mp_obj_new_exception_msg(MP_QSTR_KeyError, "popitem(): dictionary is empty")); + } + mp_obj_dict_it_t *iter = mp_obj_new_dict_iterator(self, 0); + + mp_map_elem_t *next = dict_it_iternext_elem(iter); + self->map.used--; + mp_obj_t items[] = {next->key, next->value}; + next->key = NULL; + mp_obj_t tuple = mp_obj_new_tuple(2, items); + + return tuple; +} +static MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem); + + /******************************************************************************/ /* dict constructors & etc */ @@ -201,6 +221,7 @@ const mp_obj_type_t dict_type = { { "copy", &dict_copy_obj }, { "get", &dict_get_obj }, { "pop", &dict_pop_obj }, + { "popitem", &dict_popitem_obj }, { NULL, NULL }, // end-of-list sentinel }, }; diff --git a/tests/basics/tests/dict_popitem.py b/tests/basics/tests/dict_popitem.py new file mode 100644 index 000000000..184735cde --- /dev/null +++ b/tests/basics/tests/dict_popitem.py @@ -0,0 +1,11 @@ +d={1:2,3:4} +print(d.popitem()) +print(d) +print(d.popitem()) +print(d) +try: + print(d.popitem(), "!!!",) +except KeyError: + print("Raised KeyError") +else: + print("Did not raise KeyError") From be8fe5be2eb89cd8db741b16dcb50bf5966c33ae Mon Sep 17 00:00:00 2001 From: "John R. Lenton" Date: Mon, 6 Jan 2014 20:25:51 +0000 Subject: [PATCH 12/18] Added dict.setdefault --- py/map.c | 1 + py/objdict.c | 36 +++++++++++++++++++-------- tests/basics/tests/dict_setdefault.py | 13 ++++++++++ 3 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 tests/basics/tests/dict_setdefault.py diff --git a/py/map.c b/py/map.c index 8554150ad..b88181989 100644 --- a/py/map.c +++ b/py/map.c @@ -112,6 +112,7 @@ mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_n retval->key = elem->key; retval->value = elem->value; elem->key = NULL; + elem->value = NULL; return retval; } return elem; diff --git a/py/objdict.c b/py/objdict.c index b4cf8f4aa..753db7d80 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -140,27 +140,30 @@ static mp_obj_t dict_copy(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy); -static mp_obj_t dict_get_helper(mp_map_t *self, mp_obj_t key, mp_obj_t deflt, bool pop) { - mp_map_elem_t *elem = mp_map_lookup_helper(self, key, false, pop); - if (elem == NULL) { +static mp_obj_t dict_get_helper(mp_map_t *self, mp_obj_t key, mp_obj_t deflt, bool pop, bool set) { + mp_map_elem_t *elem = mp_map_lookup_helper(self, key, set, pop); + mp_obj_t value; + if (elem == NULL || elem->value == NULL) { if (deflt == NULL) { if (pop) { nlr_jump(mp_obj_new_exception_msg(MP_QSTR_KeyError, "")); } else { - return mp_const_none; + value = mp_const_none; } } else { - return deflt; + value = deflt; } } else { - mp_obj_t value = elem->value; + value = elem->value; if (pop) { /* catch the leak (from mp_map_lookup_helper) */ m_free(elem, 2 * sizeof(mp_obj_t)); } - return value; } - + if (set) { + elem->value = value; + } + return value; } static mp_obj_t dict_get(int n_args, const mp_obj_t *args) { @@ -170,7 +173,7 @@ static mp_obj_t dict_get(int n_args, const mp_obj_t *args) { return dict_get_helper(&((mp_obj_dict_t *)args[0])->map, args[1], n_args == 3 ? args[2] : NULL, - false); + false, false); } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_get_obj, 2, 3, dict_get); @@ -181,11 +184,22 @@ static mp_obj_t dict_pop(int n_args, const mp_obj_t *args) { return dict_get_helper(&((mp_obj_dict_t *)args[0])->map, args[1], n_args == 3 ? args[2] : NULL, - true); + true, false); } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_pop_obj, 2, 3, dict_pop); +static mp_obj_t dict_setdefault(int n_args, const mp_obj_t *args) { + assert(2 <= n_args && n_args <= 3); + assert(MP_OBJ_IS_TYPE(args[0], &dict_type)); + + return dict_get_helper(&((mp_obj_dict_t *)args[0])->map, + args[1], + n_args == 3 ? args[2] : NULL, + false, true); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_setdefault_obj, 2, 3, dict_setdefault); + static mp_obj_t dict_popitem(mp_obj_t self_in) { assert(MP_OBJ_IS_TYPE(self_in, &dict_type)); @@ -199,6 +213,7 @@ static mp_obj_t dict_popitem(mp_obj_t self_in) { self->map.used--; mp_obj_t items[] = {next->key, next->value}; next->key = NULL; + next->value = NULL; mp_obj_t tuple = mp_obj_new_tuple(2, items); return tuple; @@ -222,6 +237,7 @@ const mp_obj_type_t dict_type = { { "get", &dict_get_obj }, { "pop", &dict_pop_obj }, { "popitem", &dict_popitem_obj }, + { "setdefault", &dict_setdefault_obj }, { NULL, NULL }, // end-of-list sentinel }, }; diff --git a/tests/basics/tests/dict_setdefault.py b/tests/basics/tests/dict_setdefault.py new file mode 100644 index 000000000..57d0ba451 --- /dev/null +++ b/tests/basics/tests/dict_setdefault.py @@ -0,0 +1,13 @@ +d = {} +print(d.setdefault(1)) +print(d.setdefault(1)) +print(d.setdefault(5, 42)) +print(d.setdefault(5, 1)) +print(d[1]) +print(d[5]) +d.pop(5) +print(d.setdefault(5, 1)) +print(d[1]) +print(d[5]) + + From 1a9951d5aab681a4ff408d8520696b9f67b83d49 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 6 Jan 2014 22:13:00 +0000 Subject: [PATCH 13/18] py: Fix up number operations and coercion. --- examples/mandel.py | 14 +++++++ py/asmx64.c | 2 +- py/obj.h | 2 + py/objcomplex.c | 70 +++++++++++++++++---------------- py/objfloat.c | 43 ++++++++++++--------- py/runtime.c | 96 ++++++++++++++++++++++++---------------------- 6 files changed, 129 insertions(+), 98 deletions(-) create mode 100644 examples/mandel.py diff --git a/examples/mandel.py b/examples/mandel.py new file mode 100644 index 000000000..b13b7d87f --- /dev/null +++ b/examples/mandel.py @@ -0,0 +1,14 @@ +@micropython.native +def in_set(c): + z = 0 + for i in range(40): + z = z*z + c + if abs(z) > 60: + return False + return True + +for v in range(31): + line = [] + for u in range(91): + line.append('*' if in_set((u / 30 - 2) + (v / 15 - 1) * 1j) else ' ') + print(''.join(line)) diff --git a/py/asmx64.c b/py/asmx64.c index ed9ca80f5..226d4efee 100644 --- a/py/asmx64.c +++ b/py/asmx64.c @@ -155,7 +155,7 @@ void asm_x64_end_pass(asm_x64_t *as) { //as->code_base = m_new(byte, as->code_size); need to allocale executable memory uint actual_alloc; as->code_base = alloc_mem(as->code_size, &actual_alloc, true); - printf("code_size: %u\n", as->code_size); + //printf("code_size: %u\n", as->code_size); } /* diff --git a/py/obj.h b/py/obj.h index 88a611ba7..1ba7427cb 100644 --- a/py/obj.h +++ b/py/obj.h @@ -200,10 +200,12 @@ qstr mp_obj_str_get(mp_obj_t self_in); // float extern const mp_obj_type_t float_type; mp_float_t mp_obj_float_get(mp_obj_t self_in); +mp_obj_t mp_obj_float_binary_op(int op, mp_float_t lhs_val, mp_obj_t rhs); // complex extern const mp_obj_type_t complex_type; void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag); +mp_obj_t mp_obj_complex_binary_op(int op, mp_float_t lhs_real, mp_float_t lhs_imag, mp_obj_t rhs_in); #endif // tuple diff --git a/py/objcomplex.c b/py/objcomplex.c index fc32f9667..46f43b54b 100644 --- a/py/objcomplex.c +++ b/py/objcomplex.c @@ -48,14 +48,14 @@ static mp_obj_t complex_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *a { mp_float_t real, imag; if (MP_OBJ_IS_TYPE(args[1], &complex_type)) { - mp_obj_get_complex(args[1], &real, &imag); + mp_obj_complex_get(args[1], &real, &imag); } else { real = mp_obj_get_float(args[1]); imag = 0; } if (MP_OBJ_IS_TYPE(args[0], &complex_type)) { mp_float_t real2, imag2; - mp_obj_get_complex(args[0], &real2, &imag2); + mp_obj_complex_get(args[0], &real2, &imag2); real -= imag2; imag += real2; } else { @@ -80,9 +80,41 @@ static mp_obj_t complex_unary_op(int op, mp_obj_t o_in) { } static mp_obj_t complex_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { - mp_float_t lhs_real, lhs_imag, rhs_real, rhs_imag; - mp_obj_complex_get(lhs_in, &lhs_real, &lhs_imag); - mp_obj_complex_get(rhs_in, &rhs_real, &rhs_imag); + mp_obj_complex_t *lhs = lhs_in; + return mp_obj_complex_binary_op(op, lhs->real, lhs->imag, rhs_in); +} + +const mp_obj_type_t complex_type = { + { &mp_const_type }, + "complex", + complex_print, // print + complex_make_new, // make_new + NULL, // call_n + complex_unary_op, // unary_op + complex_binary_op, // binary_op + NULL, // getiter + NULL, // iternext + .methods = { { NULL, NULL }, }, +}; + +mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag) { + mp_obj_complex_t *o = m_new_obj(mp_obj_complex_t); + o->base.type = &complex_type; + o->real = real; + o->imag = imag; + return o; +} + +void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag) { + assert(MP_OBJ_IS_TYPE(self_in, &complex_type)); + mp_obj_complex_t *self = self_in; + *real = self->real; + *imag = self->imag; +} + +mp_obj_t mp_obj_complex_binary_op(int op, mp_float_t lhs_real, mp_float_t lhs_imag, mp_obj_t rhs_in) { + mp_float_t rhs_real, rhs_imag; + mp_obj_get_complex(rhs_in, &rhs_real, &rhs_imag); // can be any type, this function will convert to float (if possible) switch (op) { case RT_BINARY_OP_ADD: case RT_BINARY_OP_INPLACE_ADD: @@ -115,32 +147,4 @@ static mp_obj_t complex_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { return mp_obj_new_complex(lhs_real, lhs_imag); } -const mp_obj_type_t complex_type = { - { &mp_const_type }, - "complex", - complex_print, // print - complex_make_new, // make_new - NULL, // call_n - complex_unary_op, // unary_op - complex_binary_op, // binary_op - NULL, // getiter - NULL, // iternext - .methods = { { NULL, NULL }, }, -}; - -mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag) { - mp_obj_complex_t *o = m_new_obj(mp_obj_complex_t); - o->base.type = &complex_type; - o->real = real; - o->imag = imag; - return o; -} - -void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag) { - assert(MP_OBJ_IS_TYPE(self_in, &complex_type)); - mp_obj_complex_t *self = self_in; - *real = self->real; - *imag = self->imag; -} - #endif diff --git a/py/objfloat.c b/py/objfloat.c index 0250172ad..336ae597f 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -53,27 +53,12 @@ static mp_obj_t float_unary_op(int op, mp_obj_t o_in) { } static mp_obj_t float_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + mp_obj_float_t *lhs = lhs_in; if (MP_OBJ_IS_TYPE(rhs_in, &complex_type)) { - return complex_type.binary_op(op, lhs_in, rhs_in); + return mp_obj_complex_binary_op(op, lhs->value, 0, rhs_in); + } else { + return mp_obj_float_binary_op(op, lhs->value, rhs_in); } - mp_float_t lhs_val = mp_obj_get_float(lhs_in); - mp_float_t rhs_val = mp_obj_get_float(rhs_in); - switch (op) { - case RT_BINARY_OP_ADD: - case RT_BINARY_OP_INPLACE_ADD: lhs_val += rhs_val; break; - case RT_BINARY_OP_SUBTRACT: - case RT_BINARY_OP_INPLACE_SUBTRACT: lhs_val -= rhs_val; break; - case RT_BINARY_OP_MULTIPLY: - case RT_BINARY_OP_INPLACE_MULTIPLY: lhs_val *= rhs_val; break; - /* TODO floor(?) the value - case RT_BINARY_OP_FLOOR_DIVIDE: - case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: val = lhs_val / rhs_val; break; - */ - case RT_BINARY_OP_TRUE_DIVIDE: - case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: lhs_val /= rhs_val; break; - return NULL; // op not supported - } - return mp_obj_new_float(lhs_val); } const mp_obj_type_t float_type = { @@ -99,4 +84,24 @@ mp_float_t mp_obj_float_get(mp_obj_t self_in) { return self->value; } +mp_obj_t mp_obj_float_binary_op(int op, mp_float_t lhs_val, mp_obj_t rhs_in) { + mp_float_t rhs_val = mp_obj_get_float(rhs_in); // can be any type, this function will convert to float (if possible) + switch (op) { + case RT_BINARY_OP_ADD: + case RT_BINARY_OP_INPLACE_ADD: lhs_val += rhs_val; break; + case RT_BINARY_OP_SUBTRACT: + case RT_BINARY_OP_INPLACE_SUBTRACT: lhs_val -= rhs_val; break; + case RT_BINARY_OP_MULTIPLY: + case RT_BINARY_OP_INPLACE_MULTIPLY: lhs_val *= rhs_val; break; + /* TODO floor(?) the value + case RT_BINARY_OP_FLOOR_DIVIDE: + case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: val = lhs_val / rhs_val; break; + */ + case RT_BINARY_OP_TRUE_DIVIDE: + case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: lhs_val /= rhs_val; break; + return NULL; // op not supported + } + return mp_obj_new_float(lhs_val); +} + #endif diff --git a/py/runtime.c b/py/runtime.c index 197c08b55..6bc71abff 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -452,57 +452,63 @@ mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) { // then fail // note that list does not implement + or +=, so that inplace_concat is reached first for += - if (MP_OBJ_IS_SMALL_INT(lhs) && MP_OBJ_IS_SMALL_INT(rhs)) { + if (MP_OBJ_IS_SMALL_INT(lhs)) { mp_small_int_t lhs_val = MP_OBJ_SMALL_INT_VALUE(lhs); - mp_small_int_t rhs_val = MP_OBJ_SMALL_INT_VALUE(rhs); - switch (op) { - case RT_BINARY_OP_OR: - case RT_BINARY_OP_INPLACE_OR: lhs_val |= rhs_val; break; - case RT_BINARY_OP_XOR: - case RT_BINARY_OP_INPLACE_XOR: lhs_val ^= rhs_val; break; - case RT_BINARY_OP_AND: - case RT_BINARY_OP_INPLACE_AND: lhs_val &= rhs_val; break; - case RT_BINARY_OP_LSHIFT: - case RT_BINARY_OP_INPLACE_LSHIFT: lhs_val <<= rhs_val; break; - case RT_BINARY_OP_RSHIFT: - case RT_BINARY_OP_INPLACE_RSHIFT: lhs_val >>= rhs_val; break; - case RT_BINARY_OP_ADD: - case RT_BINARY_OP_INPLACE_ADD: lhs_val += rhs_val; break; - case RT_BINARY_OP_SUBTRACT: - case RT_BINARY_OP_INPLACE_SUBTRACT: lhs_val -= rhs_val; break; - case RT_BINARY_OP_MULTIPLY: - case RT_BINARY_OP_INPLACE_MULTIPLY: lhs_val *= rhs_val; break; - case RT_BINARY_OP_FLOOR_DIVIDE: - case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: lhs_val /= rhs_val; break; -#if MICROPY_ENABLE_FLOAT - case RT_BINARY_OP_TRUE_DIVIDE: - case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: return mp_obj_new_float((mp_float_t)lhs_val / (mp_float_t)rhs_val); -#endif + if (MP_OBJ_IS_SMALL_INT(rhs)) { + mp_small_int_t rhs_val = MP_OBJ_SMALL_INT_VALUE(rhs); + switch (op) { + case RT_BINARY_OP_OR: + case RT_BINARY_OP_INPLACE_OR: lhs_val |= rhs_val; break; + case RT_BINARY_OP_XOR: + case RT_BINARY_OP_INPLACE_XOR: lhs_val ^= rhs_val; break; + case RT_BINARY_OP_AND: + case RT_BINARY_OP_INPLACE_AND: lhs_val &= rhs_val; break; + case RT_BINARY_OP_LSHIFT: + case RT_BINARY_OP_INPLACE_LSHIFT: lhs_val <<= rhs_val; break; + case RT_BINARY_OP_RSHIFT: + case RT_BINARY_OP_INPLACE_RSHIFT: lhs_val >>= rhs_val; break; + case RT_BINARY_OP_ADD: + case RT_BINARY_OP_INPLACE_ADD: lhs_val += rhs_val; break; + case RT_BINARY_OP_SUBTRACT: + case RT_BINARY_OP_INPLACE_SUBTRACT: lhs_val -= rhs_val; break; + case RT_BINARY_OP_MULTIPLY: + case RT_BINARY_OP_INPLACE_MULTIPLY: lhs_val *= rhs_val; break; + case RT_BINARY_OP_FLOOR_DIVIDE: + case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: lhs_val /= rhs_val; break; + #if MICROPY_ENABLE_FLOAT + case RT_BINARY_OP_TRUE_DIVIDE: + case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: return mp_obj_new_float((mp_float_t)lhs_val / (mp_float_t)rhs_val); + #endif - // TODO implement modulo as specified by Python - case RT_BINARY_OP_MODULO: - case RT_BINARY_OP_INPLACE_MODULO: lhs_val %= rhs_val; break; + // TODO implement modulo as specified by Python + case RT_BINARY_OP_MODULO: + case RT_BINARY_OP_INPLACE_MODULO: lhs_val %= rhs_val; break; - // TODO check for negative power, and overflow - case RT_BINARY_OP_POWER: - case RT_BINARY_OP_INPLACE_POWER: - { - int ans = 1; - while (rhs_val > 0) { - if (rhs_val & 1) { - ans *= lhs_val; + // TODO check for negative power, and overflow + case RT_BINARY_OP_POWER: + case RT_BINARY_OP_INPLACE_POWER: + { + int ans = 1; + while (rhs_val > 0) { + if (rhs_val & 1) { + ans *= lhs_val; + } + lhs_val *= lhs_val; + rhs_val /= 2; } - lhs_val *= lhs_val; - rhs_val /= 2; + lhs_val = ans; + break; } - lhs_val = ans; - break; - } - default: assert(0); - } - if (fit_small_int(lhs_val)) { - return MP_OBJ_NEW_SMALL_INT(lhs_val); + default: assert(0); + } + if (fit_small_int(lhs_val)) { + return MP_OBJ_NEW_SMALL_INT(lhs_val); + } + } else if (MP_OBJ_IS_TYPE(rhs, &float_type)) { + return mp_obj_float_binary_op(op, lhs_val, rhs); + } else if (MP_OBJ_IS_TYPE(rhs, &complex_type)) { + return mp_obj_complex_binary_op(op, lhs_val, 0, rhs); } } else if (MP_OBJ_IS_OBJ(lhs)) { mp_obj_base_t *o = lhs; From e3096172c90fb1223f99bc6f957322009c79c36f Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 6 Jan 2014 22:17:37 +0000 Subject: [PATCH 14/18] stm: Re-fix LED defines. --- stm/led.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stm/led.c b/stm/led.c index 19ce99cec..d4bc0a0c8 100644 --- a/stm/led.c +++ b/stm/led.c @@ -8,10 +8,10 @@ #include "led.h" /* LED numbers, used internally */ -#define PYB_LED_1 (0) -#define PYB_LED_2 (1) -#define PYB_LED_3 (2) -#define PYB_LED_4 (3) +#define PYB_LED_1 (1) +#define PYB_LED_2 (2) +#define PYB_LED_3 (3) +#define PYB_LED_4 (4) #if defined(PYBOARD) #define PYB_LED1_PORT (GPIOA) From 88f3043e0a306b17d3b9bdf530da51a66ba1cccc Mon Sep 17 00:00:00 2001 From: "John R. Lenton" Date: Mon, 6 Jan 2014 22:58:17 +0000 Subject: [PATCH 15/18] added a first pass of dict.update --- py/objdict.c | 29 ++++++++++++++++++++++++++++- py/runtime.c | 4 ++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/py/objdict.c b/py/objdict.c index 753db7d80..7cfd597ed 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -157,7 +157,7 @@ static mp_obj_t dict_get_helper(mp_map_t *self, mp_obj_t key, mp_obj_t deflt, bo value = elem->value; if (pop) { /* catch the leak (from mp_map_lookup_helper) */ - m_free(elem, 2 * sizeof(mp_obj_t)); + m_free(elem, sizeof(mp_map_elem_t)); } } if (set) { @@ -220,6 +220,32 @@ static mp_obj_t dict_popitem(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem); +static mp_obj_t dict_update(mp_obj_t self_in, mp_obj_t iterable) { + assert(MP_OBJ_IS_TYPE(self_in, &dict_type)); + mp_obj_dict_t *self = self_in; + /* TODO: check for the "keys" method */ + mp_obj_t iter = rt_getiter(iterable); + mp_obj_t next = NULL; + while ((next = rt_iternext(iter)) != mp_const_stop_iteration) { + mp_obj_t inneriter = rt_getiter(next); + mp_obj_t key = rt_iternext(inneriter); + mp_obj_t value = rt_iternext(inneriter); + mp_obj_t stop = rt_iternext(inneriter); + if (key == mp_const_stop_iteration + || value == mp_const_stop_iteration + || stop != mp_const_stop_iteration) { + nlr_jump(mp_obj_new_exception_msg( + MP_QSTR_ValueError, + "dictionary update sequence has the wrong length")); + } else { + mp_map_lookup_helper(&self->map, key, true, false)->value = value; + } + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(dict_update_obj, dict_update); + /******************************************************************************/ /* dict constructors & etc */ @@ -238,6 +264,7 @@ const mp_obj_type_t dict_type = { { "pop", &dict_pop_obj }, { "popitem", &dict_popitem_obj }, { "setdefault", &dict_setdefault_obj }, + { "update", &dict_update_obj }, { NULL, NULL }, // end-of-list sentinel }, }; diff --git a/py/runtime.c b/py/runtime.c index 6bc71abff..f3fabc39c 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -859,13 +859,13 @@ mp_obj_t rt_getiter(mp_obj_t o_in) { mp_obj_t rt_iternext(mp_obj_t o_in) { if (MP_OBJ_IS_SMALL_INT(o_in)) { - nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "? 'int' object is not iterable")); + nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "'int' object is not an iterator")); } else { mp_obj_base_t *o = o_in; if (o->type->iternext != NULL) { return o->type->iternext(o_in); } else { - nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "? '%s' object is not iterable", o->type->name)); + nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "'%s' object is not an iterator", o->type->name)); } } } From 27d4ca7693c276d09a911c00c3442729c516dc23 Mon Sep 17 00:00:00 2001 From: "John R. Lenton" Date: Tue, 7 Jan 2014 22:51:51 +0000 Subject: [PATCH 16/18] forgot to add test for dict.update --- tests/basics/tests/dict_update.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/basics/tests/dict_update.py diff --git a/tests/basics/tests/dict_update.py b/tests/basics/tests/dict_update.py new file mode 100644 index 000000000..e7ae0bd96 --- /dev/null +++ b/tests/basics/tests/dict_update.py @@ -0,0 +1,10 @@ +d = {1:2, 3:4} +print(d) +d.update(["ab"]) +print(d[1]) +print(d[3]) +print(d["a"]) +print(len(d)) +d.update([(1,4)]) +print(d[1]) +print(len(d)) From 9c83ec0edac2394431a5d1aecba1d666ffdea0a3 Mon Sep 17 00:00:00 2001 From: "John R. Lenton" Date: Tue, 7 Jan 2014 23:06:46 +0000 Subject: [PATCH 17/18] Merge remote-tracking branch 'upstream/master' into dict_feats --- examples/accellog.py | 14 + examples/conwaylife.py | 43 + examples/lcd.py | 36 + examples/ledangle.py | 22 + examples/mandel.py | 32 +- examples/pyb.py | 11 + logo/vector-logo-2-BW.svg | 587 ++++++++++ logo/vector-logo-2.png | Bin 0 -> 90783 bytes logo/vector-logo-inkscape_master.svg | 1471 ++++++++++++++++++++++++++ py/asmx64.c | 8 +- py/gc.c | 4 + py/lexerunix.c | 5 + py/misc.h | 6 +- py/mpconfig.h | 89 +- py/mpqstrraw.h | 1 + py/obj.h | 47 +- py/objbool.c | 10 +- py/objboundmeth.c | 9 +- py/objcell.c | 8 - py/objclass.c | 9 +- py/objclosure.c | 9 +- py/objcomplex.c | 12 +- py/objexcept.c | 9 +- py/objfloat.c | 1 - py/objfun.c | 56 +- py/objgenerator.c | 20 +- py/objinstance.c | 8 - py/objint.c | 1 - py/objlist.c | 68 +- py/objmodule.c | 13 +- py/objnone.c | 1 - py/objrange.c | 18 +- py/objset.c | 10 +- py/objslice.c | 10 +- py/objstr.c | 13 +- py/objtuple.c | 6 +- py/objtype.c | 1 - py/py.mk | 105 ++ py/repl.c | 5 + py/repl.h | 2 + py/runtime.c | 62 +- py/showbc.c | 4 + stm/Makefile | 95 +- stm/i2c.c | 18 +- stm/led.c | 12 +- stm/main.c | 22 +- stm/mpconfigport.h | 6 +- stm/servo.c | 10 +- tests/basics/tests/list_sort.py | 13 + unix-cpy/Makefile | 94 +- unix-cpy/main.c | 1 - unix-cpy/mpconfigport.h | 6 +- unix/Makefile | 99 +- unix/main.c | 163 +-- unix/mpconfigport.h | 6 +- 55 files changed, 2762 insertions(+), 629 deletions(-) create mode 100644 examples/accellog.py create mode 100644 examples/conwaylife.py create mode 100644 examples/lcd.py create mode 100644 examples/ledangle.py create mode 100644 examples/pyb.py create mode 100644 logo/vector-logo-2-BW.svg create mode 100644 logo/vector-logo-2.png create mode 100644 logo/vector-logo-inkscape_master.svg create mode 100644 py/py.mk create mode 100644 tests/basics/tests/list_sort.py diff --git a/examples/accellog.py b/examples/accellog.py new file mode 100644 index 000000000..81f44f19a --- /dev/null +++ b/examples/accellog.py @@ -0,0 +1,14 @@ +# log the accelerometer values to a file, 1 per second + +f = open('motion.dat', 'w') # open the file for writing + +for i in range(60): # loop 60 times + time = pyb.time() # get the current time + accel = pyb.accel() # get the accelerometer data + + # write time and x,y,z values to the file + f.write('{} {} {} {}\n'.format(time, accel[0], accel[1], accel[2])) + + pyb.delay(1000) # wait 1000 ms = 1 second + +f.close() # close the file diff --git a/examples/conwaylife.py b/examples/conwaylife.py new file mode 100644 index 000000000..fb62ce69e --- /dev/null +++ b/examples/conwaylife.py @@ -0,0 +1,43 @@ +# do 1 iteration of Conway's Game of Life +def conway_step(): + for x in range(128): # loop over x coordinates + for y in range(32): # loop over y coordinates + # count number of neigbours + num_neighbours = (lcd.get(x - 1, y - 1) + + lcd.get(x, y - 1) + + lcd.get(x + 1, y - 1) + + lcd.get(x - 1, y) + + lcd.get(x + 1, y) + + lcd.get(x + 1, y + 1) + + lcd.get(x, y + 1) + + lcd.get(x - 1, y + 1)) + + # check if the centre cell is alive or not + self = lcd.get(x, y) + + # apply the rules of life + if self and not (2 <= num_neighbours <= 3): + lcd.reset(x, y) # not enough, or too many neighbours: cell dies + elif not self and num_neighbours == 3: + lcd.set(x, y) # exactly 3 neigbours around an empty cell: cell is born + +# randomise the start +def conway_rand(): + lcd.clear() # clear the LCD + for x in range(128): # loop over x coordinates + for y in range(32): # loop over y coordinates + if pyb.rand() & 1: # get a 1-bit random number + lcd.set(x, y) # set the pixel randomly + +# loop for a certain number of frames, doing iterations of Conway's Game of Life +def conway_go(num_frames): + for i in range(num_frames): + conway_step() # do 1 iteration + lcd.show() # update the LCD + +# PC testing +import lcd +import pyb +lcd = lcd.LCD(128, 32) +conway_rand() +conway_go(100) diff --git a/examples/lcd.py b/examples/lcd.py new file mode 100644 index 000000000..3303337bf --- /dev/null +++ b/examples/lcd.py @@ -0,0 +1,36 @@ +# LCD testing object for PC +# uses double buffering +class LCD: + def __init__(self, width, height): + self.width = width + self.height = height + self.buf1 = [[0 for x in range(self.width)] for y in range(self.height)] + self.buf2 = [[0 for x in range(self.width)] for y in range(self.height)] + + def clear(self): + for y in range(self.height): + for x in range(self.width): + self.buf1[y][x] = self.buf2[y][x] = 0 + + def show(self): + print('') # blank line to separate frames + for y in range(self.height): + for x in range(self.width): + self.buf1[y][x] = self.buf2[y][x] + for y in range(self.height): + row = ''.join(['*' if self.buf1[y][x] else ' ' for x in range(self.width)]) + print(row) + + def get(self, x, y): + if 0 <= x < self.width and 0 <= y < self.height: + return self.buf1[y][x] + else: + return 0 + + def reset(self, x, y): + if 0 <= x < self.width and 0 <= y < self.height: + self.buf2[y][x] = 0 + + def set(self, x, y): + if 0 <= x < self.width and 0 <= y < self.height: + self.buf2[y][x] = 1 diff --git a/examples/ledangle.py b/examples/ledangle.py new file mode 100644 index 000000000..35c9148a4 --- /dev/null +++ b/examples/ledangle.py @@ -0,0 +1,22 @@ +def led_angle(seconds_to_run_for): + # make LED objects + l1 = pyb.Led(1) + l2 = pyb.Led(2) + + for i in range(20 * seconds_to_run_for): + # get x-axis + accel = pyb.accel()[0] + + # turn on LEDs depending on angle + if accel < -10: + l1.on() + l2.off() + elif accel > 10: + l1.off() + l2.on() + else: + l1.off() + l2.off() + + # delay so that loop runs at at 1/50ms = 20Hz + pyb.delay(50) diff --git a/examples/mandel.py b/examples/mandel.py index b13b7d87f..996132a91 100644 --- a/examples/mandel.py +++ b/examples/mandel.py @@ -1,14 +1,22 @@ -@micropython.native -def in_set(c): - z = 0 - for i in range(40): - z = z*z + c - if abs(z) > 60: - return False - return True +def mandelbrot(): + # returns True if c, complex, is in the Mandelbrot set + @micropython.native + def in_set(c): + z = 0 + for i in range(40): + z = z*z + c + if abs(z) > 60: + return False + return True -for v in range(31): - line = [] + lcd.clear() for u in range(91): - line.append('*' if in_set((u / 30 - 2) + (v / 15 - 1) * 1j) else ' ') - print(''.join(line)) + for v in range(31): + if in_set((u / 30 - 2) + (v / 15 - 1) * 1j): + lcd.set(u, v) + lcd.show() + +# PC testing +import lcd +lcd = lcd.LCD(128, 32) +mandelbrot() diff --git a/examples/pyb.py b/examples/pyb.py new file mode 100644 index 000000000..5e24f40e4 --- /dev/null +++ b/examples/pyb.py @@ -0,0 +1,11 @@ +# pyboard testing functions for PC + +def delay(n): + pass + +rand_seed = 1 +def rand(): + global rand_seed + # for these choice of numbers, see P L'Ecuyer, "Tables of linear congruential generators of different sizes and good lattice structure" + rand_seed = (rand_seed * 653276) % 8388593 + return rand_seed diff --git a/logo/vector-logo-2-BW.svg b/logo/vector-logo-2-BW.svg new file mode 100644 index 000000000..d7ff920f9 --- /dev/null +++ b/logo/vector-logo-2-BW.svg @@ -0,0 +1,587 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logo/vector-logo-2.png b/logo/vector-logo-2.png new file mode 100644 index 0000000000000000000000000000000000000000..3ad2383ff55529517a04245157d25834c161e230 GIT binary patch literal 90783 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/py/asmx64.c b/py/asmx64.c index 226d4efee..d3158170f 100644 --- a/py/asmx64.c +++ b/py/asmx64.c @@ -1,16 +1,18 @@ #include #include -#include -#include #include #include "misc.h" -#include "asmx64.h" #include "mpconfig.h" // wrapper around everything in this file #if MICROPY_EMIT_X64 +#include +#include + +#include "asmx64.h" + #if defined(__OpenBSD__) || defined(__MACH__) #define MAP_ANONYMOUS MAP_ANON #endif diff --git a/py/gc.c b/py/gc.c index 7d4cac6e7..054a3cc31 100644 --- a/py/gc.c +++ b/py/gc.c @@ -6,6 +6,8 @@ #include "mpconfig.h" #include "gc.h" +#if MICROPY_ENABLE_GC + // a machine word is big enough to hold a pointer /* #define BYTES_PER_WORD (8) @@ -380,3 +382,5 @@ int main(void) { gc_dump_at(); } */ + +#endif // MICROPY_ENABLE_GC diff --git a/py/lexerunix.c b/py/lexerunix.c index 14c28c16d..5336610ba 100644 --- a/py/lexerunix.c +++ b/py/lexerunix.c @@ -4,8 +4,11 @@ #include #include "misc.h" +#include "mpconfig.h" #include "lexer.h" +#if MICROPY_ENABLE_LEXER_UNIX + typedef struct _str_buf_t { bool free; // free src_beg when done const char *src_beg; // beginning of source @@ -78,3 +81,5 @@ mp_lexer_t *mp_import_open_file(qstr mod_name) { vstr_printf(vstr, "%s.py", qstr_str(mod_name)); return mp_lexer_new_from_file(vstr_str(vstr)); // TODO does lexer need to copy the string? can we free it here? } + +#endif // MICROPY_ENABLE_LEXER_UNIX diff --git a/py/misc.h b/py/misc.h index 153218ba2..1bf4d8f29 100644 --- a/py/misc.h +++ b/py/misc.h @@ -5,11 +5,7 @@ /** types *******************************************************/ -typedef int bool; -enum { - false = 0, - true = 1 -}; +#include typedef unsigned char byte; typedef unsigned int uint; diff --git a/py/mpconfig.h b/py/mpconfig.h index 56495d915..2017ba366 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -4,8 +4,80 @@ #include -#ifndef INT_FMT +// Any options not explicitly set in mpconfigport.h will get default +// values below. + +/*****************************************************************************/ +/* Micro Python emitters */ + +// Whether to emit CPython byte codes (for debugging/testing) +// Enabling this overrides all other emitters +#ifndef MICROPY_EMIT_CPYTHON +#define MICROPY_EMIT_CPYTHON (0) +#endif + +// Whether to emit x64 native code +#ifndef MICROPY_EMIT_X64 +#define MICROPY_EMIT_X64 (0) +#endif + +// Whether to emit thumb native code +#ifndef MICROPY_EMIT_THUMB +#define MICROPY_EMIT_THUMB (0) +#endif + +// Whether to enable the thumb inline assembler +#ifndef MICROPY_EMIT_INLINE_THUMB +#define MICROPY_EMIT_INLINE_THUMB (0) +#endif + +/*****************************************************************************/ +/* Internal debugging stuff */ + +// Whether to collect memory allocation stats +#ifndef MICROPY_MEM_STATS +#define MICROPY_MEM_STATS (0) +#endif + +// Whether to build code to show byte code +#ifndef MICROPY_SHOW_BC +#define MICROPY_SHOW_BC (0) +#endif + +/*****************************************************************************/ +/* Fine control over Python features */ + +// Whether to include the garbage collector +#ifndef MICROPY_ENABLE_GC +#define MICROPY_ENABLE_GC (0) +#endif + +// Whether to include REPL helper function +#ifndef MICROPY_ENABLE_REPL_HELPERS +#define MICROPY_ENABLE_REPL_HELPERS (0) +#endif + +// Whether to include lexer helper function for unix +#ifndef MICROPY_ENABLE_LEXER_UNIX +#define MICROPY_ENABLE_LEXER_UNIX (0) +#endif + +// Whether to support float and complex types +#ifndef MICROPY_ENABLE_FLOAT +#define MICROPY_ENABLE_FLOAT (0) +#endif + +// Whether to support slice object and correspondingly +// slice subscript operators +#ifndef MICROPY_ENABLE_SLICE +#define MICROPY_ENABLE_SLICE (1) +#endif + +/*****************************************************************************/ +/* Miscellaneous settings */ + // printf format spec to use for machine_int_t and friends +#ifndef INT_FMT #ifdef __LP64__ // Archs where machine_int_t == long, long != int #define UINT_FMT "%lu" @@ -16,18 +88,3 @@ #define INT_FMT "%d" #endif #endif //INT_FMT - - -// Any options not explicitly set in mpconfigport.h will get default -// values below. - -// Whether to collect memory allocation stats -#ifndef MICROPY_MEM_STATS -#define MICROPY_MEM_STATS (1) -#endif - -// Whether to support slice object and correspondingly -// slice subscript operators -#ifndef MICROPY_ENABLE_SLICE -#define MICROPY_ENABLE_SLICE (1) -#endif diff --git a/py/mpqstrraw.h b/py/mpqstrraw.h index fe74c3e92..eeed6f3a0 100644 --- a/py/mpqstrraw.h +++ b/py/mpqstrraw.h @@ -30,6 +30,7 @@ Q(NameError) Q(SyntaxError) Q(TypeError) Q(ValueError) +Q(OSError) Q(abs) Q(all) diff --git a/py/obj.h b/py/obj.h index 1ba7427cb..332867a19 100644 --- a/py/obj.h +++ b/py/obj.h @@ -15,15 +15,14 @@ typedef machine_int_t mp_small_int_t; typedef machine_float_t mp_float_t; #endif -// Anything that wants to be a Micro Python object must -// have mp_obj_base_t as its first member (except NULL and small ints) - -typedef struct _mp_obj_base_t mp_obj_base_t; -typedef struct _mp_obj_type_t mp_obj_type_t; +// Anything that wants to be a Micro Python object must have +// mp_obj_base_t as its first member (except NULL and small ints) +struct _mp_obj_type_t; struct _mp_obj_base_t { - const mp_obj_type_t *type; + const struct _mp_obj_type_t *type; }; +typedef struct _mp_obj_base_t mp_obj_base_t; // The NULL object is used to indicate the absence of an object // It *cannot* be used when an mp_obj_t is expected, except where explicitly allowed @@ -43,12 +42,17 @@ struct _mp_obj_base_t { #define MP_DECLARE_CONST_FUN_OBJ(obj_name) extern const mp_obj_fun_native_t obj_name -#define MP_DEFINE_CONST_FUN_OBJ_0(obj_name, fun_name) const mp_obj_fun_native_t obj_name = {{&fun_native_type}, 0, 0, fun_name} -#define MP_DEFINE_CONST_FUN_OBJ_1(obj_name, fun_name) const mp_obj_fun_native_t obj_name = {{&fun_native_type}, 1, 1, fun_name} -#define MP_DEFINE_CONST_FUN_OBJ_2(obj_name, fun_name) const mp_obj_fun_native_t obj_name = {{&fun_native_type}, 2, 2, fun_name} -#define MP_DEFINE_CONST_FUN_OBJ_3(obj_name, fun_name) const mp_obj_fun_native_t obj_name = {{&fun_native_type}, 3, 3, fun_name} -#define MP_DEFINE_CONST_FUN_OBJ_VAR(obj_name, n_args_min, fun_name) const mp_obj_fun_native_t obj_name = {{&fun_native_type}, n_args_min, (~((machine_uint_t)0)), fun_name} -#define MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name, n_args_min, n_args_max, fun_name) const mp_obj_fun_native_t obj_name = {{&fun_native_type}, n_args_min, n_args_max, fun_name} +#define MP_DEFINE_CONST_FUN_OBJ_VOID_PTR(obj_name, is_kw, n_args_min, n_args_max, fun_name) const mp_obj_fun_native_t obj_name = {{&fun_native_type}, is_kw, n_args_min, n_args_max, (void *)fun_name} +#define MP_DEFINE_CONST_FUN_OBJ_0(obj_name, fun_name) MP_DEFINE_CONST_FUN_OBJ_VOID_PTR(obj_name, false, 0, 0, (mp_fun_0_t)fun_name) +#define MP_DEFINE_CONST_FUN_OBJ_1(obj_name, fun_name) MP_DEFINE_CONST_FUN_OBJ_VOID_PTR(obj_name, false, 1, 1, (mp_fun_1_t)fun_name) +#define MP_DEFINE_CONST_FUN_OBJ_2(obj_name, fun_name) MP_DEFINE_CONST_FUN_OBJ_VOID_PTR(obj_name, false, 2, 2, (mp_fun_2_t)fun_name) +#define MP_DEFINE_CONST_FUN_OBJ_3(obj_name, fun_name) MP_DEFINE_CONST_FUN_OBJ_VOID_PTR(obj_name, false, 3, 3, (mp_fun_3_t)fun_name) +#define MP_DEFINE_CONST_FUN_OBJ_VAR(obj_name, n_args_min, fun_name) MP_DEFINE_CONST_FUN_OBJ_VOID_PTR(obj_name, false, n_args_min, (~((machine_uint_t)0)), (mp_fun_var_t)fun_name) +#define MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name, n_args_min, n_args_max, fun_name) MP_DEFINE_CONST_FUN_OBJ_VOID_PTR(obj_name, false, n_args_min, n_args_max, (mp_fun_var_t)fun_name) +#define MP_DEFINE_CONST_FUN_OBJ_KW(obj_name, fun_name) MP_DEFINE_CONST_FUN_OBJ_VOID_PTR(obj_name, true, 0, (~((machine_uint_t)0)), (mp_fun_var_t)fun_name) + +// Need to declare this here so we are not dependent on map.h +struct _mp_map_t; // Type definitions for methods @@ -58,10 +62,12 @@ typedef mp_obj_t (*mp_fun_2_t)(mp_obj_t, mp_obj_t); typedef mp_obj_t (*mp_fun_3_t)(mp_obj_t, mp_obj_t, mp_obj_t); typedef mp_obj_t (*mp_fun_t)(void); typedef mp_obj_t (*mp_fun_var_t)(int n, const mp_obj_t *); +typedef mp_obj_t (*mp_fun_kw_t)(mp_obj_t*, struct _mp_map_t*); typedef void (*mp_print_fun_t)(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o); typedef mp_obj_t (*mp_make_new_fun_t)(mp_obj_t type_in, int n_args, const mp_obj_t *args); // args are in reverse order in the array typedef mp_obj_t (*mp_call_n_fun_t)(mp_obj_t fun, int n_args, const mp_obj_t *args); // args are in reverse order in the array +typedef mp_obj_t (*mp_call_n_kw_fun_t)(mp_obj_t fun, int n_args, int n_kw, const mp_obj_t *args); // args are in reverse order in the array typedef mp_obj_t (*mp_unary_op_fun_t)(int op, mp_obj_t); typedef mp_obj_t (*mp_binary_op_fun_t)(int op, mp_obj_t, mp_obj_t); @@ -77,13 +83,14 @@ struct _mp_obj_type_t { mp_make_new_fun_t make_new; // to make an instance of the type mp_call_n_fun_t call_n; + mp_call_n_kw_fun_t call_n_kw; mp_unary_op_fun_t unary_op; // can return NULL if op not supported mp_binary_op_fun_t binary_op; // can return NULL if op not supported mp_fun_1_t getiter; mp_fun_1_t iternext; - const mp_method_t methods[]; + const mp_method_t *methods; /* What we might need to add here: @@ -108,6 +115,8 @@ struct _mp_obj_type_t { */ }; +typedef struct _mp_obj_type_t mp_obj_type_t; + // Constant objects, globally accessible extern const mp_obj_type_t mp_const_type; @@ -118,10 +127,6 @@ extern const mp_obj_t mp_const_empty_tuple; extern const mp_obj_t mp_const_ellipsis; extern const mp_obj_t mp_const_stop_iteration; // special object indicating end of iteration (not StopIteration exception!) -// Need to declare this here so we are not dependent on map.h - -struct _mp_map_t; - // General API for objects mp_obj_t mp_obj_new_none(void); @@ -144,8 +149,8 @@ mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun); mp_obj_t mp_obj_new_gen_wrap(uint n_locals, uint n_stack, mp_obj_t fun); mp_obj_t mp_obj_new_gen_instance(const byte *bytecode, uint n_state, int n_args, const mp_obj_t *args); mp_obj_t mp_obj_new_closure(mp_obj_t fun, mp_obj_t closure_tuple); -mp_obj_t mp_obj_new_tuple(uint n, mp_obj_t *items); -mp_obj_t mp_obj_new_tuple_reverse(uint n, mp_obj_t *items); +mp_obj_t mp_obj_new_tuple(uint n, const mp_obj_t *items); +mp_obj_t mp_obj_new_tuple_reverse(uint n, const mp_obj_t *items); mp_obj_t mp_obj_new_list(uint n, mp_obj_t *items); mp_obj_t mp_obj_new_list_reverse(uint n, mp_obj_t *items); mp_obj_t mp_obj_new_dict(int n_args); @@ -234,13 +239,15 @@ void mp_obj_slice_get(mp_obj_t self_in, machine_int_t *start, machine_int_t *sto // functions typedef struct _mp_obj_fun_native_t { // need this so we can define const objects (to go in ROM) mp_obj_base_t base; - machine_uint_t n_args_min; // inclusive + bool is_kw : 1; + machine_uint_t n_args_min : (sizeof(machine_uint_t) - 1); // inclusive machine_uint_t n_args_max; // inclusive void *fun; // TODO add mp_map_t *globals // for const function objects, make an empty, const map // such functions won't be able to access the global scope, but that's probably okay } mp_obj_fun_native_t; + extern const mp_obj_type_t fun_native_type; extern const mp_obj_type_t fun_bc_type; void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte **code); diff --git a/py/objbool.c b/py/objbool.c index 77394dab9..cfab7db06 100644 --- a/py/objbool.c +++ b/py/objbool.c @@ -34,14 +34,8 @@ static mp_obj_t bool_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args const mp_obj_type_t bool_type = { { &mp_const_type }, "bool", - bool_print, // print - bool_make_new, // make_new - NULL, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = {{NULL, NULL},}, + .print = bool_print, + .make_new = bool_make_new, }; static const mp_obj_bool_t false_obj = {{&bool_type}, false}; diff --git a/py/objboundmeth.c b/py/objboundmeth.c index 4e6d2e010..78e5c6249 100644 --- a/py/objboundmeth.c +++ b/py/objboundmeth.c @@ -36,14 +36,7 @@ mp_obj_t bound_meth_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) { const mp_obj_type_t bound_meth_type = { { &mp_const_type }, "bound_method", - NULL, // print - NULL, // make_new - bound_meth_call_n, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = {{NULL, NULL},}, + .call_n = bound_meth_call_n, }; mp_obj_t mp_obj_new_bound_meth(mp_obj_t self, mp_obj_t meth) { diff --git a/py/objcell.c b/py/objcell.c index 3465f9919..264125bf3 100644 --- a/py/objcell.c +++ b/py/objcell.c @@ -26,14 +26,6 @@ void mp_obj_cell_set(mp_obj_t self_in, mp_obj_t obj) { const mp_obj_type_t cell_type = { { &mp_const_type }, "cell", - NULL, // print - NULL, // make_new - NULL, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = {{NULL, NULL},}, }; mp_obj_t mp_obj_new_cell(mp_obj_t obj) { diff --git a/py/objclass.c b/py/objclass.c index 536abdf7e..d13d1775a 100644 --- a/py/objclass.c +++ b/py/objclass.c @@ -63,14 +63,7 @@ mp_map_t *mp_obj_class_get_locals(mp_obj_t self_in) { const mp_obj_type_t class_type = { { &mp_const_type }, "class", - NULL, // print - NULL, // make_new - class_call_n, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = {{NULL, NULL},}, + .call_n = class_call_n, }; mp_obj_t mp_obj_new_class(mp_map_t *class_locals) { diff --git a/py/objclosure.c b/py/objclosure.c index 3317eb86f..b372ee6fe 100644 --- a/py/objclosure.c +++ b/py/objclosure.c @@ -35,14 +35,7 @@ mp_obj_t closure_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) { const mp_obj_type_t closure_type = { { &mp_const_type }, "closure", - NULL, // print - NULL, // make_new - closure_call_n, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = {{NULL, NULL},}, + .call_n = closure_call_n, }; mp_obj_t mp_obj_new_closure(mp_obj_t fun, mp_obj_t closure_tuple) { diff --git a/py/objcomplex.c b/py/objcomplex.c index 46f43b54b..1036bae42 100644 --- a/py/objcomplex.c +++ b/py/objcomplex.c @@ -87,14 +87,10 @@ static mp_obj_t complex_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { const mp_obj_type_t complex_type = { { &mp_const_type }, "complex", - complex_print, // print - complex_make_new, // make_new - NULL, // call_n - complex_unary_op, // unary_op - complex_binary_op, // binary_op - NULL, // getiter - NULL, // iternext - .methods = { { NULL, NULL }, }, + .print = complex_print, + .make_new = complex_make_new, + .unary_op = complex_unary_op, + .binary_op = complex_binary_op, }; mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag) { diff --git a/py/objexcept.c b/py/objexcept.c index 22b8c5812..829b14785 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -38,14 +38,7 @@ void exception_print(void (*print)(void *env, const char *fmt, ...), void *env, const mp_obj_type_t exception_type = { { &mp_const_type }, "exception", - exception_print, // print - NULL, // make_new - NULL, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = {{NULL, NULL},}, + .print = exception_print, }; mp_obj_t mp_obj_new_exception(qstr id) { diff --git a/py/objfloat.c b/py/objfloat.c index 336ae597f..d21472d5d 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -68,7 +68,6 @@ const mp_obj_type_t float_type = { .make_new = float_make_new, .unary_op = float_unary_op, .binary_op = float_binary_op, - .methods = { { NULL, NULL }, }, }; mp_obj_t mp_obj_new_float(mp_float_t value) { diff --git a/py/objfun.c b/py/objfun.c index 0db6459a3..395824b04 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -17,9 +17,13 @@ // mp_obj_fun_native_t defined in obj.h +mp_obj_t fun_native_call_n_kw(mp_obj_t self_in, int n_args, int n_kw, const mp_obj_t *args); // args are in reverse order in the array mp_obj_t fun_native_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) { mp_obj_fun_native_t *self = self_in; + if (self->is_kw) { + return fun_native_call_n_kw(self_in, n_args, 0, args); + } if (self->n_args_min == self->n_args_max) { // function requires a fixed number of arguments @@ -69,19 +73,29 @@ mp_obj_t fun_native_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) { } } +mp_obj_t fun_native_call_n_kw(mp_obj_t self_in, int n_args, int n_kw, const mp_obj_t *args) { + mp_obj_fun_native_t *self = self_in; + + if (!self->is_kw) { + nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "function does not take keyword arguments")); + } + + mp_obj_t *vargs = mp_obj_new_tuple_reverse(n_args, args + 2*n_kw); + mp_map_t *kw_args = mp_map_new(MP_MAP_QSTR, n_kw); + for (int i = 0; i < 2*n_kw; i+=2) { + qstr name = mp_obj_str_get(args[i+1]); + mp_qstr_map_lookup(kw_args, name, true)->value = args[i]; + } + mp_obj_t res = ((mp_fun_kw_t)self->fun)(vargs, kw_args); + /* TODO clean up vargs and kw_args */ + return res; +} + const mp_obj_type_t fun_native_type = { { &mp_const_type }, "function", - NULL, // print - NULL, // make_new - fun_native_call_n, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = { - {NULL, NULL}, // end-of-list sentinel - }, + .call_n = fun_native_call_n, + .call_n_kw = fun_native_call_n_kw, }; mp_obj_t rt_make_function_0(mp_fun_0_t fun) { @@ -174,16 +188,7 @@ mp_obj_t fun_bc_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) { const mp_obj_type_t fun_bc_type = { { &mp_const_type }, "function", - NULL, // print - NULL, // make_new - fun_bc_call_n, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = { - {NULL, NULL}, // end-of-list sentinel - }, + .call_n = fun_bc_call_n, }; mp_obj_t mp_obj_new_fun_bc(int n_args, uint n_state, const byte *code) { @@ -288,16 +293,7 @@ mp_obj_t fun_asm_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) { static const mp_obj_type_t fun_asm_type = { { &mp_const_type }, "function", - NULL, // print - NULL, // make_new - fun_asm_call_n, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = { - {NULL, NULL}, // end-of-list sentinel - }, + .call_n = fun_asm_call_n, }; mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun) { diff --git a/py/objgenerator.c b/py/objgenerator.c index a91b0d6fc..7eeb4ef80 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -39,14 +39,7 @@ mp_obj_t gen_wrap_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) { const mp_obj_type_t gen_wrap_type = { { &mp_const_type }, "generator", - NULL, // print - NULL, // make_new - gen_wrap_call_n, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = {{NULL, NULL},}, + .call_n = gen_wrap_call_n, }; mp_obj_t mp_obj_new_gen_wrap(uint n_locals, uint n_stack, mp_obj_t fun) { @@ -94,14 +87,9 @@ mp_obj_t gen_instance_iternext(mp_obj_t self_in) { const mp_obj_type_t gen_instance_type = { { &mp_const_type }, "generator", - gen_instance_print, // print - NULL, // make_new - NULL, // call_n - NULL, // unary_op - NULL, // binary_op - gen_instance_getiter, // getiter - gen_instance_iternext, // iternext - .methods = {{NULL, NULL},}, + .print = gen_instance_print, + .getiter = gen_instance_getiter, + .iternext = gen_instance_iternext, }; // args are in reverse order in the array diff --git a/py/objinstance.c b/py/objinstance.c index 8ef2773fd..209643e2b 100644 --- a/py/objinstance.c +++ b/py/objinstance.c @@ -92,14 +92,6 @@ void mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { const mp_obj_type_t instance_type = { { &mp_const_type }, "instance", - NULL, // print - NULL, // make_new - NULL, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = {{NULL, NULL},}, }; mp_obj_t mp_obj_new_instance(mp_obj_t class) { diff --git a/py/objint.c b/py/objint.c index 8d69c4e7a..9cd5ebae2 100644 --- a/py/objint.c +++ b/py/objint.c @@ -34,7 +34,6 @@ const mp_obj_type_t int_type = { { &mp_const_type }, "int", .make_new = int_make_new, - .methods = { { NULL, NULL }, }, }; mp_obj_t mp_obj_new_int(machine_int_t value) { diff --git a/py/objlist.c b/py/objlist.c index 02a6b1525..5162fa09f 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -8,6 +8,7 @@ #include "mpconfig.h" #include "mpqstr.h" #include "obj.h" +#include "map.h" #include "runtime0.h" #include "runtime.h" @@ -57,6 +58,7 @@ static mp_obj_t list_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args default: nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "list takes at most 1 argument, %d given", (void*)(machine_int_t)n_args)); } + return NULL; } static mp_obj_t list_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) { @@ -119,14 +121,15 @@ static mp_obj_t list_pop(int n_args, const mp_obj_t *args) { } // TODO make this conform to CPython's definition of sort -static void mp_quicksort(mp_obj_t *head, mp_obj_t *tail, mp_obj_t key_fn) { +static void mp_quicksort(mp_obj_t *head, mp_obj_t *tail, mp_obj_t key_fn, bool reversed) { + int op = reversed ? RT_COMPARE_OP_MORE : RT_COMPARE_OP_LESS; while (head < tail) { mp_obj_t *h = head - 1; mp_obj_t *t = tail; - mp_obj_t v = rt_call_function_1(key_fn, tail[0]); // get pivot using key_fn + mp_obj_t v = key_fn == NULL ? tail[0] : rt_call_function_1(key_fn, tail[0]); // get pivot using key_fn for (;;) { - do ++h; while (rt_compare_op(RT_COMPARE_OP_LESS, rt_call_function_1(key_fn, h[0]), v) == mp_const_true); - do --t; while (h < t && rt_compare_op(RT_COMPARE_OP_LESS, v, rt_call_function_1(key_fn, t[0])) == mp_const_true); + do ++h; while (rt_compare_op(op, key_fn == NULL ? h[0] : rt_call_function_1(key_fn, h[0]), v) == mp_const_true); + do --t; while (h < t && rt_compare_op(op, v, key_fn == NULL ? t[0] : rt_call_function_1(key_fn, t[0])) == mp_const_true); if (h >= t) break; mp_obj_t x = h[0]; h[0] = t[0]; @@ -135,16 +138,31 @@ static void mp_quicksort(mp_obj_t *head, mp_obj_t *tail, mp_obj_t key_fn) { mp_obj_t x = h[0]; h[0] = tail[0]; tail[0] = x; - mp_quicksort(head, t, key_fn); + mp_quicksort(head, t, key_fn, reversed); head = h + 1; } } -static mp_obj_t list_sort(mp_obj_t self_in, mp_obj_t key_fn) { - assert(MP_OBJ_IS_TYPE(self_in, &list_type)); - mp_obj_list_t *self = self_in; +static mp_obj_t list_sort(mp_obj_t *args, mp_map_t *kwargs) { + mp_obj_t *args_items = NULL; + machine_uint_t args_len = 0; + qstr key_idx = qstr_from_str_static("key"); + qstr reverse_idx = qstr_from_str_static("reverse"); + + assert(MP_OBJ_IS_TYPE(args, &tuple_type)); + mp_obj_tuple_get(args, &args_len, &args_items); + assert(args_len >= 1); + if (args_len > 1) { + nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, + "list.sort takes no positional arguments")); + } + mp_obj_list_t *self = args_items[0]; if (self->len > 1) { - mp_quicksort(self->items, self->items + self->len - 1, key_fn); + mp_map_elem_t *keyfun = mp_qstr_map_lookup(kwargs, key_idx, false); + mp_map_elem_t *reverse = mp_qstr_map_lookup(kwargs, reverse_idx, false); + mp_quicksort(self->items, self->items + self->len - 1, + keyfun ? keyfun->value : NULL, + reverse && reverse->value ? rt_is_true(reverse->value) : false); } return mp_const_none; // return None, as per CPython } @@ -258,29 +276,30 @@ static MP_DEFINE_CONST_FUN_OBJ_3(list_insert_obj, list_insert); static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(list_pop_obj, 1, 2, list_pop); static MP_DEFINE_CONST_FUN_OBJ_2(list_remove_obj, list_remove); static MP_DEFINE_CONST_FUN_OBJ_1(list_reverse_obj, list_reverse); -static MP_DEFINE_CONST_FUN_OBJ_2(list_sort_obj, list_sort); +static MP_DEFINE_CONST_FUN_OBJ_KW(list_sort_obj, list_sort); + +static const mp_method_t list_type_methods[] = { + { "append", &list_append_obj }, + { "clear", &list_clear_obj }, + { "copy", &list_copy_obj }, + { "count", &list_count_obj }, + { "index", &list_index_obj }, + { "insert", &list_insert_obj }, + { "pop", &list_pop_obj }, + { "remove", &list_remove_obj }, + { "reverse", &list_reverse_obj }, + { "sort", &list_sort_obj }, + { NULL, NULL }, // end-of-list sentinel +}; const mp_obj_type_t list_type = { { &mp_const_type }, "list", .print = list_print, .make_new = list_make_new, - .unary_op = NULL, .binary_op = list_binary_op, .getiter = list_getiter, - .methods = { - { "append", &list_append_obj }, - { "clear", &list_clear_obj }, - { "copy", &list_copy_obj }, - { "count", &list_count_obj }, - { "index", &list_index_obj }, - { "insert", &list_insert_obj }, - { "pop", &list_pop_obj }, - { "remove", &list_remove_obj }, - { "reverse", &list_reverse_obj }, - { "sort", &list_sort_obj }, - { NULL, NULL }, // end-of-list sentinel - }, + .methods = list_type_methods, }; static mp_obj_list_t *list_new(uint n) { @@ -344,7 +363,6 @@ static const mp_obj_type_t list_it_type = { { &mp_const_type }, "list_iterator", .iternext = list_it_iternext, - .methods = { { NULL, NULL }, }, }; mp_obj_t mp_obj_new_list_iterator(mp_obj_list_t *list, int cur) { diff --git a/py/objmodule.c b/py/objmodule.c index 2d6f9555e..7b92b76f4 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -6,6 +6,7 @@ #include "nlr.h" #include "misc.h" #include "mpconfig.h" +#include "mpqstr.h" #include "obj.h" #include "runtime.h" #include "map.h" @@ -24,21 +25,15 @@ void module_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_ const mp_obj_type_t module_type = { { &mp_const_type }, "module", - module_print, // print - NULL, // make_new - NULL, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = {{NULL, NULL},}, + .print = module_print, }; mp_obj_t mp_obj_new_module(qstr module_name) { mp_obj_module_t *o = m_new_obj(mp_obj_module_t); o->base.type = &module_type; o->name = module_name; - o->globals = mp_map_new(MP_MAP_QSTR, 0); + o->globals = mp_map_new(MP_MAP_QSTR, 1); + mp_qstr_map_lookup(o->globals, MP_QSTR___name__, true)->value = mp_obj_new_str(module_name); return o; } diff --git a/py/objnone.c b/py/objnone.c index 877e61fe5..c0efe6c0f 100644 --- a/py/objnone.c +++ b/py/objnone.c @@ -18,7 +18,6 @@ const mp_obj_type_t none_type = { { &mp_const_type }, "NoneType", .print = none_print, - .methods = {{NULL, NULL},}, }; static const mp_obj_none_t none_obj = {{&none_type}}; diff --git a/py/objrange.c b/py/objrange.c index 1ef42673f..a2a0e67b0 100644 --- a/py/objrange.c +++ b/py/objrange.c @@ -25,14 +25,7 @@ mp_obj_t range_getiter(mp_obj_t o_in) { static const mp_obj_type_t range_type = { { &mp_const_type} , "range", - NULL, // print - NULL, // make_new - NULL, // call_n - NULL, // unary_op - NULL, // binary_op - range_getiter, - NULL, // iternext - .methods = {{NULL, NULL},}, + .getiter = range_getiter, }; // range is a class and instances are immutable sequence objects @@ -70,14 +63,7 @@ mp_obj_t range_it_iternext(mp_obj_t o_in) { static const mp_obj_type_t range_it_type = { { &mp_const_type }, "range_iterator", - NULL, // print - NULL, // make_new - NULL, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - range_it_iternext, - .methods = {{NULL, NULL},}, + .iternext = range_it_iternext, }; mp_obj_t mp_obj_new_range_iterator(int cur, int stop, int step) { diff --git a/py/objset.c b/py/objset.c index dd9a4d1e1..67dab11df 100644 --- a/py/objset.c +++ b/py/objset.c @@ -57,14 +57,8 @@ static mp_obj_t set_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args) const mp_obj_type_t set_type = { { &mp_const_type }, "set", - set_print, // print - set_make_new, // make_new - NULL, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = { { NULL, NULL }, }, + .print = set_print, + .make_new = set_make_new, }; mp_obj_t mp_obj_new_set(int n_args, mp_obj_t *items) { diff --git a/py/objslice.c b/py/objslice.c index da69b92af..d95ccef06 100644 --- a/py/objslice.c +++ b/py/objslice.c @@ -23,14 +23,7 @@ void ellipsis_print(void (*print)(void *env, const char *fmt, ...), void *env, m const mp_obj_type_t ellipsis_type = { { &mp_const_type }, "ellipsis", - ellipsis_print, // print - NULL, // make_new - NULL, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = {{NULL, NULL},}, + .print = ellipsis_print, }; static const mp_obj_ellipsis_t ellipsis_obj = {{&ellipsis_type}}; @@ -58,7 +51,6 @@ const mp_obj_type_t slice_type = { { &mp_const_type }, "slice", .print = slice_print, - .methods = { { NULL, NULL }, }, }; // TODO: Make sure to handle "empty" values, which are signified by None in CPython diff --git a/py/objstr.c b/py/objstr.c index db3e0beca..cc9f7f85b 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -184,17 +184,19 @@ mp_obj_t str_format(int n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_2(str_join_obj, str_join); static MP_DEFINE_CONST_FUN_OBJ_VAR(str_format_obj, 1, str_format); +static const mp_method_t str_type_methods[] = { + { "join", &str_join_obj }, + { "format", &str_format_obj }, + { NULL, NULL }, // end-of-list sentinel +}; + const mp_obj_type_t str_type = { { &mp_const_type }, "str", .print = str_print, .binary_op = str_binary_op, .getiter = str_getiter, - .methods = { - { "join", &str_join_obj }, - { "format", &str_format_obj }, - { NULL, NULL }, // end-of-list sentinel - }, + .methods = str_type_methods, }; mp_obj_t mp_obj_new_str(qstr qstr) { @@ -235,7 +237,6 @@ static const mp_obj_type_t str_it_type = { { &mp_const_type }, "str_iterator", .iternext = str_it_iternext, - .methods = { { NULL, NULL }, }, }; mp_obj_t mp_obj_new_str_iterator(mp_obj_str_t *str, int cur) { diff --git a/py/objtuple.c b/py/objtuple.c index ceca4200e..0050fc5ea 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -103,14 +103,13 @@ const mp_obj_type_t tuple_type = { .make_new = tuple_make_new, .binary_op = tuple_binary_op, .getiter = tuple_getiter, - .methods = {{NULL, NULL},}, }; // the zero-length tuple static const mp_obj_tuple_t empty_tuple_obj = {{&tuple_type}, 0}; const mp_obj_t mp_const_empty_tuple = (mp_obj_t)&empty_tuple_obj; -mp_obj_t mp_obj_new_tuple(uint n, mp_obj_t *items) { +mp_obj_t mp_obj_new_tuple(uint n, const mp_obj_t *items) { if (n == 0) { return mp_const_empty_tuple; } @@ -123,7 +122,7 @@ mp_obj_t mp_obj_new_tuple(uint n, mp_obj_t *items) { return o; } -mp_obj_t mp_obj_new_tuple_reverse(uint n, mp_obj_t *items) { +mp_obj_t mp_obj_new_tuple_reverse(uint n, const mp_obj_t *items) { if (n == 0) { return mp_const_empty_tuple; } @@ -166,7 +165,6 @@ static const mp_obj_type_t tuple_it_type = { { &mp_const_type }, "tuple_iterator", .iternext = tuple_it_iternext, - .methods = {{NULL, NULL},}, }; static mp_obj_t mp_obj_new_tuple_iterator(mp_obj_tuple_t *tuple, int cur) { diff --git a/py/objtype.c b/py/objtype.c index 37d40b38f..8778c180c 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -27,5 +27,4 @@ const mp_obj_type_t mp_const_type = { "type", .print = type_print, .call_n = type_call_n, - .methods = {{NULL, NULL},}, }; diff --git a/py/py.mk b/py/py.mk new file mode 100644 index 000000000..3ed8a3e3d --- /dev/null +++ b/py/py.mk @@ -0,0 +1,105 @@ +# default settings; can be overriden in main Makefile + +ifndef PY_SRC +PY_SRC = ../py +endif + +ifndef BUILD +BUILD = build +endif + +# to create the build directory + +$(BUILD): + mkdir -p $@ + +# where py object files go (they have a name prefix to prevent filename clashes) + +PY_BUILD = $(BUILD)/py. + +# py object files + +PY_O_BASENAME = \ + nlrx86.o \ + nlrx64.o \ + nlrthumb.o \ + malloc.o \ + gc.o \ + qstr.o \ + vstr.o \ + unicode.o \ + lexer.o \ + lexerunix.o \ + parse.o \ + scope.o \ + compile.o \ + emitcommon.o \ + emitpass1.o \ + emitcpy.o \ + emitbc.o \ + asmx64.o \ + emitnx64.o \ + asmthumb.o \ + emitnthumb.o \ + emitinlinethumb.o \ + runtime.o \ + map.o \ + obj.o \ + objbool.o \ + objboundmeth.o \ + objcell.o \ + objclass.o \ + objclosure.o \ + objcomplex.o \ + objdict.o \ + objexcept.o \ + objfloat.o \ + objfun.o \ + objgenerator.o \ + objinstance.o \ + objint.o \ + objlist.o \ + objmodule.o \ + objnone.o \ + objrange.o \ + objset.o \ + objslice.o \ + objstr.o \ + objtuple.o \ + objtype.o \ + builtin.o \ + builtinimport.o \ + vm.o \ + showbc.o \ + repl.o \ + +# prepend the build destination prefix to the py object files + +PY_O = $(addprefix $(PY_BUILD), $(PY_O_BASENAME)) + +$(PY_BUILD)emitnx64.o: $(PY_SRC)/emitnative.c $(PY_SRC)/emit.h mpconfigport.h + $(CC) $(CFLAGS) -DN_X64 -c -o $@ $< + +$(PY_BUILD)emitnthumb.o: $(PY_SRC)/emitnative.c $(PY_SRC)/emit.h mpconfigport.h + $(CC) $(CFLAGS) -DN_THUMB -c -o $@ $< + +$(PY_BUILD)%.o: $(PY_SRC)/%.S + $(CC) $(CFLAGS) -c -o $@ $< + +$(PY_BUILD)%.o: $(PY_SRC)/%.c mpconfigport.h + $(CC) $(CFLAGS) -c -o $@ $< + +# optimising gc for speed; 5ms down to 4ms on pybv2 +$(PY_BUILD)gc.o: $(PY_SRC)/gc.c + $(CC) $(CFLAGS) -O3 -c -o $@ $< + +# optimising vm for speed, adds only a small amount to code size but makes a huge difference to speed (20% faster) +$(PY_BUILD)vm.o: $(PY_SRC)/vm.c + $(CC) $(CFLAGS) -O3 -c -o $@ $< + +# header dependencies + +$(PY_BUILD)parse.o: $(PY_SRC)/grammar.h +$(PY_BUILD)compile.o: $(PY_SRC)/grammar.h +$(PY_BUILD)/emitcpy.o: $(PY_SRC)/emit.h +$(PY_BUILD)emitbc.o: $(PY_SRC)/emit.h diff --git a/py/repl.c b/py/repl.c index 4241ef0e4..473313c1e 100644 --- a/py/repl.c +++ b/py/repl.c @@ -1,6 +1,9 @@ #include "misc.h" +#include "mpconfig.h" #include "repl.h" +#if MICROPY_ENABLE_REPL_HELPERS + bool str_startswith_word(const char *str, const char *head) { int i; for (i = 0; str[i] && head[i]; i++) { @@ -42,3 +45,5 @@ bool mp_repl_is_compound_stmt(const char *line) { } return n_paren > 0 || n_brack > 0 || n_brace > 0; } + +#endif // MICROPY_ENABLE_REPL_HELPERS diff --git a/py/repl.h b/py/repl.h index 02fe523ed..23259fa90 100644 --- a/py/repl.h +++ b/py/repl.h @@ -1 +1,3 @@ +#if MICROPY_ENABLE_REPL_HELPERS bool mp_repl_is_compound_stmt(const char *line); +#endif diff --git a/py/runtime.c b/py/runtime.c index f3fabc39c..5c476d9ec 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -61,7 +61,8 @@ typedef struct _mp_code_t { } mp_code_t; static int next_unique_code_id; -static mp_code_t *unique_codes; +static machine_uint_t unique_codes_alloc = 0; +static mp_code_t *unique_codes = NULL; #ifdef WRITE_CODE FILE *fp_write_code = NULL; @@ -83,6 +84,7 @@ void rt_init(void) { mp_qstr_map_lookup(&map_builtins, MP_QSTR_TypeError, true)->value = mp_obj_new_exception(MP_QSTR_TypeError); mp_qstr_map_lookup(&map_builtins, MP_QSTR_SyntaxError, true)->value = mp_obj_new_exception(MP_QSTR_SyntaxError); mp_qstr_map_lookup(&map_builtins, MP_QSTR_ValueError, true)->value = mp_obj_new_exception(MP_QSTR_ValueError); + mp_qstr_map_lookup(&map_builtins, MP_QSTR_OSError, true)->value = mp_obj_new_exception(MP_QSTR_OSError); // built-in objects mp_qstr_map_lookup(&map_builtins, MP_QSTR_Ellipsis, true)->value = mp_const_ellipsis; @@ -126,6 +128,7 @@ void rt_init(void) { mp_qstr_map_lookup(&map_builtins, MP_QSTR_sum, true)->value = rt_make_function_var(1, mp_builtin_sum); next_unique_code_id = 1; // 0 indicates "no code" + unique_codes_alloc = 0; unique_codes = NULL; #ifdef WRITE_CODE @@ -134,6 +137,7 @@ void rt_init(void) { } void rt_deinit(void) { + m_del(mp_code_t, unique_codes, unique_codes_alloc); #ifdef WRITE_CODE if (fp_write_code != NULL) { fclose(fp_write_code); @@ -146,18 +150,20 @@ int rt_get_unique_code_id(void) { } static void alloc_unique_codes(void) { - if (unique_codes == NULL) { - unique_codes = m_new(mp_code_t, next_unique_code_id + 10); // XXX hack until we fix the REPL allocation problem - for (int i = 0; i < next_unique_code_id; i++) { + if (next_unique_code_id > unique_codes_alloc) { + // increase size of unique_codes table + unique_codes = m_renew(mp_code_t, unique_codes, unique_codes_alloc, next_unique_code_id); + for (int i = unique_codes_alloc; i < next_unique_code_id; i++) { unique_codes[i].kind = MP_CODE_NONE; } + unique_codes_alloc = next_unique_code_id; } } void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_stack, bool is_generator) { alloc_unique_codes(); - assert(1 <= unique_code_id && unique_code_id < next_unique_code_id); + assert(1 <= unique_code_id && unique_code_id < next_unique_code_id && unique_codes[unique_code_id].kind == MP_CODE_NONE); unique_codes[unique_code_id].kind = MP_CODE_BYTE; unique_codes[unique_code_id].n_args = n_args; unique_codes[unique_code_id].n_locals = n_locals; @@ -192,7 +198,7 @@ void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, i void rt_assign_native_code(int unique_code_id, void *fun, uint len, int n_args) { alloc_unique_codes(); - assert(1 <= unique_code_id && unique_code_id < next_unique_code_id); + assert(1 <= unique_code_id && unique_code_id < next_unique_code_id && unique_codes[unique_code_id].kind == MP_CODE_NONE); unique_codes[unique_code_id].kind = MP_CODE_NATIVE; unique_codes[unique_code_id].n_args = n_args; unique_codes[unique_code_id].n_locals = 0; @@ -225,7 +231,7 @@ void rt_assign_native_code(int unique_code_id, void *fun, uint len, int n_args) void rt_assign_inline_asm_code(int unique_code_id, void *fun, uint len, int n_args) { alloc_unique_codes(); - assert(1 <= unique_code_id && unique_code_id < next_unique_code_id); + assert(1 <= unique_code_id && unique_code_id < next_unique_code_id && unique_codes[unique_code_id].kind == MP_CODE_NONE); unique_codes[unique_code_id].kind = MP_CODE_INLINE_ASM; unique_codes[unique_code_id].n_args = n_args; unique_codes[unique_code_id].n_locals = 0; @@ -683,10 +689,20 @@ mp_obj_t rt_call_function_n(mp_obj_t fun_in, int n_args, const mp_obj_t *args) { // args are in reverse order in the array; keyword arguments come first, value then key // eg: (value1, key1, value0, key0, arg1, arg0) -mp_obj_t rt_call_function_n_kw(mp_obj_t fun, uint n_args, uint n_kw, const mp_obj_t *args) { - // TODO - assert(0); - return mp_const_none; +mp_obj_t rt_call_function_n_kw(mp_obj_t fun_in, uint n_args, uint n_kw, const mp_obj_t *args) { + // TODO merge this and _n into a single, smarter thing + DEBUG_OP_printf("calling function %p(n_args=%d, n_kw=%d, args=%p)\n", fun_in, n_args, n_kw, args); + + if (MP_OBJ_IS_SMALL_INT(fun_in)) { + nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "'int' object is not callable")); + } else { + mp_obj_base_t *fun = fun_in; + if (fun->type->call_n_kw != NULL) { + return fun->type->call_n_kw(fun_in, n_args, n_kw, args); + } else { + nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "'%s' object is not callable", fun->type->name)); + } + } } // args contains: arg(n_args-1) arg(n_args-2) ... arg(0) self/NULL fun @@ -775,10 +791,12 @@ mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) { } else if (MP_OBJ_IS_OBJ(base)) { // generic method lookup mp_obj_base_t *o = base; - const mp_method_t *meth = &o->type->methods[0]; - for (; meth->name != NULL; meth++) { - if (strcmp(meth->name, qstr_str(attr)) == 0) { - return mp_obj_new_bound_meth(base, (mp_obj_t)meth->fun); + const mp_method_t *meth = o->type->methods; + if (meth != NULL) { + for (; meth->name != NULL; meth++) { + if (strcmp(meth->name, qstr_str(attr)) == 0) { + return mp_obj_new_bound_meth(base, (mp_obj_t)meth->fun); + } } } } @@ -799,12 +817,14 @@ void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) { } else if (MP_OBJ_IS_OBJ(base)) { // generic method lookup mp_obj_base_t *o = base; - const mp_method_t *meth = &o->type->methods[0]; - for (; meth->name != NULL; meth++) { - if (strcmp(meth->name, qstr_str(attr)) == 0) { - dest[1] = (mp_obj_t)meth->fun; - dest[0] = base; - return; + const mp_method_t *meth = o->type->methods; + if (meth != NULL) { + for (; meth->name != NULL; meth++) { + if (strcmp(meth->name, qstr_str(attr)) == 0) { + dest[1] = (mp_obj_t)meth->fun; + dest[0] = base; + return; + } } } } diff --git a/py/showbc.c b/py/showbc.c index eb7d41b24..aea0aff67 100644 --- a/py/showbc.c +++ b/py/showbc.c @@ -8,6 +8,8 @@ #include "mpconfig.h" #include "bc0.h" +#if MICROPY_SHOW_BC + #define DECODE_UINT do { unum = *ip++; if (unum > 127) { unum = ((unum & 0x3f) << 8) | (*ip++); } } while (0) #define DECODE_ULABEL do { unum = (ip[0] | (ip[1] << 8)); ip += 2; } while (0) #define DECODE_SLABEL do { unum = (ip[0] | (ip[1] << 8)) - 0x8000; ip += 2; } while (0) @@ -363,3 +365,5 @@ void mp_show_byte_code(const byte *ip, int len) { printf("\n"); } } + +#endif // MICROPY_SHOW_BC diff --git a/stm/Makefile b/stm/Makefile index cd998dd88..fecd52527 100644 --- a/stm/Makefile +++ b/stm/Makefile @@ -1,9 +1,16 @@ +# define main target +all: all2 + +# include py core make definitions +include ../py/py.mk + +# program for deletion +RM = /bin/rm + STMSRC=lib #STMOTGSRC=lib-otg FATFSSRC=fatfs CC3KSRC=cc3k -PYSRC=../py -BUILD=build DFU=../tools/dfu.py TARGET=PYBOARD @@ -11,7 +18,7 @@ AS = arm-none-eabi-as CC = arm-none-eabi-gcc LD = arm-none-eabi-ld CFLAGS_CORTEX_M4 = -mthumb -mtune=cortex-m4 -mabi=aapcs-linux -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -fsingle-precision-constant -Wdouble-promotion -DSTM32F40XX -DHSE_VALUE=8000000 -CFLAGS = -I. -I$(PYSRC) -I$(FATFSSRC) -I$(STMSRC) -Wall -ansi -std=gnu99 -Os -DNDEBUG $(CFLAGS_CORTEX_M4) -D$(TARGET) +CFLAGS = -I. -I$(PY_SRC) -I$(FATFSSRC) -I$(STMSRC) -Wall -ansi -std=gnu99 -Os -DNDEBUG $(CFLAGS_CORTEX_M4) -D$(TARGET) #CFLAGS += -I$(STMOTGSRC) -DUSE_HOST_MODE -DUSE_OTG_MODE LDFLAGS = --nostdlib -T stm32f405.ld @@ -43,53 +50,6 @@ SRC_S = \ startup_stm32f40xx.s \ gchelper.s \ -PY_O = \ - nlrthumb.o \ - gc.o \ - malloc.o \ - qstr.o \ - vstr.o \ - unicode.o \ - lexer.o \ - parse.o \ - scope.o \ - compile.o \ - emitcommon.o \ - emitpass1.o \ - emitbc.o \ - asmthumb.o \ - emitnthumb.o \ - emitinlinethumb.o \ - runtime.o \ - map.o \ - obj.o \ - objbool.o \ - objboundmeth.o \ - objcell.o \ - objclass.o \ - objclosure.o \ - objcomplex.o \ - objdict.o \ - objexcept.o \ - objfloat.o \ - objfun.o \ - objgenerator.o \ - objinstance.o \ - objint.o \ - objlist.o \ - objmodule.o \ - objnone.o \ - objrange.o \ - objset.o \ - objslice.o \ - objstr.o \ - objtuple.o \ - objtype.o \ - builtin.o \ - builtinimport.o \ - vm.o \ - repl.o \ - SRC_FATFS = \ ff.c \ diskio.c \ @@ -146,10 +106,10 @@ SRC_CC3K = \ ccspi.c \ pybcc3k.c \ -OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(SRC_S:.s=.o) $(PY_O) $(SRC_FATFS:.c=.o) $(SRC_STM:.c=.o) $(SRC_CC3K:.c=.o)) +OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(SRC_S:.s=.o) $(SRC_FATFS:.c=.o) $(SRC_STM:.c=.o) $(SRC_CC3K:.c=.o)) $(PY_O) #OBJ += $(addprefix $(BUILD)/, $(SRC_STM_OTG:.c=.o)) -all: $(BUILD) $(BUILD)/flash.dfu +all2: $(BUILD) $(BUILD)/flash.dfu $(BUILD)/flash.dfu: $(BUILD)/flash0.bin $(BUILD)/flash1.bin python $(DFU) -b 0x08000000:$(BUILD)/flash0.bin -b 0x08020000:$(BUILD)/flash1.bin $@ @@ -164,9 +124,6 @@ $(BUILD)/flash.elf: $(OBJ) $(LD) $(LDFLAGS) -o $@ $(OBJ) arm-none-eabi-size $@ -$(BUILD): - mkdir -p $@ - $(BUILD)/%.o: %.s $(AS) -o $@ $< @@ -185,31 +142,7 @@ $(BUILD)/%.o: $(STMSRC)/%.c $(BUILD)/%.o: $(CC3KSRC)/%.c $(CC) $(CFLAGS) -c -o $@ $< -$(BUILD)/%.o: $(PYSRC)/%.s - $(AS) -o $@ $< - -$(BUILD)/%.o: $(PYSRC)/%.S - $(CC) $(CFLAGS) -c -o $@ $< - -$(BUILD)/%.o: $(PYSRC)/%.c mpconfigport.h - $(CC) $(CFLAGS) -c -o $@ $< - -$(BUILD)/emitnthumb.o: $(PYSRC)/emitnative.c $(PYSRC)/emit.h - $(CC) $(CFLAGS) -DN_THUMB -c -o $@ $< - -# optimising gc for speed; 5ms down to 4ms -$(BUILD)/gc.o: $(PYSRC)/gc.c - $(CC) $(CFLAGS) -O3 -c -o $@ $< - -# optimising vm for speed, adds only a small amount to code size but makes a huge difference to speed (20% faster) -$(BUILD)/vm.o: $(PYSRC)/vm.c - $(CC) $(CFLAGS) -O3 -c -o $@ $< - -$(BUILD)/parse.o: $(PYSRC)/grammar.h -$(BUILD)/compile.o: $(PYSRC)/grammar.h -$(BUILD)/emitbc.o: $(PYSRC)/emit.h - clean: - /bin/rm -rf $(BUILD) + $(RM) -rf $(BUILD) -.PHONY: all clean +.PHONY: all all2 clean diff --git a/stm/i2c.c b/stm/i2c.c index 9e25ff983..22c908566 100644 --- a/stm/i2c.c +++ b/stm/i2c.c @@ -326,18 +326,20 @@ static MP_DEFINE_CONST_FUN_OBJ_1(i2c_obj_read_obj, i2c_obj_read); static MP_DEFINE_CONST_FUN_OBJ_1(i2c_obj_readAndStop_obj, i2c_obj_readAndStop); static MP_DEFINE_CONST_FUN_OBJ_1(i2c_obj_stop_obj, i2c_obj_stop); +static const mp_method_t i2c_methods[] = { + { "start", &i2c_obj_start_obj }, + { "write", &i2c_obj_write_obj }, + { "read", &i2c_obj_read_obj }, + { "readAndStop", &i2c_obj_readAndStop_obj }, + { "stop", &i2c_obj_stop_obj }, + { NULL, NULL }, +}; + static const mp_obj_type_t i2c_obj_type = { { &mp_const_type }, "I2C", .print = i2c_obj_print, - .methods = { - { "start", &i2c_obj_start_obj }, - { "write", &i2c_obj_write_obj }, - { "read", &i2c_obj_read_obj }, - { "readAndStop", &i2c_obj_readAndStop_obj }, - { "stop", &i2c_obj_stop_obj }, - { NULL, NULL }, - } + .methods = i2c_methods, }; // create the I2C object diff --git a/stm/led.c b/stm/led.c index d4bc0a0c8..eb7c76ef1 100644 --- a/stm/led.c +++ b/stm/led.c @@ -176,15 +176,17 @@ mp_obj_t led_obj_off(mp_obj_t self_in) { static MP_DEFINE_CONST_FUN_OBJ_1(led_obj_on_obj, led_obj_on); static MP_DEFINE_CONST_FUN_OBJ_1(led_obj_off_obj, led_obj_off); +static const mp_method_t led_methods[] = { + { "on", &led_obj_on_obj }, + { "off", &led_obj_off_obj }, + { NULL, NULL }, +}; + static const mp_obj_type_t led_obj_type = { { &mp_const_type }, "Led", .print = led_obj_print, - .methods = { - { "on", &led_obj_on_obj }, - { "off", &led_obj_off_obj }, - { NULL, NULL }, - } + .methods = led_methods, }; mp_obj_t pyb_Led(mp_obj_t led_id) { diff --git a/stm/main.c b/stm/main.c index 07c974eff..a038c89b7 100644 --- a/stm/main.c +++ b/stm/main.c @@ -689,22 +689,18 @@ static MP_DEFINE_CONST_FUN_OBJ_1(file_obj_close_obj, file_obj_close); // TODO gc hook to close the file if not already closed +static const mp_method_t file_methods[] = { + { "read", &file_obj_read_obj }, + { "write", &file_obj_write_obj }, + { "close", &file_obj_close_obj }, + {NULL, NULL}, +}; + static const mp_obj_type_t file_obj_type = { { &mp_const_type }, "File", - file_obj_print, // print - NULL, // make_new - NULL, // call_n - NULL, // unary_op - NULL, // binary_op - NULL, // getiter - NULL, // iternext - .methods = { - { "read", &file_obj_read_obj }, - { "write", &file_obj_write_obj }, - { "close", &file_obj_close_obj }, - {NULL, NULL}, - } + .print = file_obj_print, + .methods = file_methods, }; mp_obj_t pyb_io_open(mp_obj_t o_filename, mp_obj_t o_mode) { diff --git a/stm/mpconfigport.h b/stm/mpconfigport.h index 4cea332f3..dfa46cc50 100644 --- a/stm/mpconfigport.h +++ b/stm/mpconfigport.h @@ -2,11 +2,11 @@ // options to control how Micro Python is built -#define MICROPY_ENABLE_FLOAT (1) -#define MICROPY_EMIT_CPYTHON (0) -#define MICROPY_EMIT_X64 (0) #define MICROPY_EMIT_THUMB (1) #define MICROPY_EMIT_INLINE_THUMB (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_ENABLE_REPL_HELPERS (1) +#define MICROPY_ENABLE_FLOAT (1) // type definitions for the specific machine diff --git a/stm/servo.c b/stm/servo.c index 31190ce79..cc9633a99 100644 --- a/stm/servo.c +++ b/stm/servo.c @@ -137,14 +137,16 @@ static mp_obj_t servo_obj_angle(mp_obj_t self_in, mp_obj_t angle) { static MP_DEFINE_CONST_FUN_OBJ_2(servo_obj_angle_obj, servo_obj_angle); +static const mp_method_t servo_methods[] = { + { "angle", &servo_obj_angle_obj }, + { NULL, NULL }, +}; + static const mp_obj_type_t servo_obj_type = { { &mp_const_type }, "Servo", .print = servo_obj_print, - .methods = { - { "angle", &servo_obj_angle_obj }, - { NULL, NULL }, - } + .methods = servo_methods, }; mp_obj_t pyb_Servo(mp_obj_t servo_id) { diff --git a/tests/basics/tests/list_sort.py b/tests/basics/tests/list_sort.py new file mode 100644 index 000000000..eff12b9c8 --- /dev/null +++ b/tests/basics/tests/list_sort.py @@ -0,0 +1,13 @@ +l = [1, 3, 2, 5] +print(l) +l.sort() +print(l) +l.sort(key=lambda x: -x) +print(l) +l.sort(key=lambda x: -x, reverse=True) +print(l) +l.sort(reverse=True) +print(l) +l.sort(reverse=False) +print(l) + diff --git a/unix-cpy/Makefile b/unix-cpy/Makefile index a1eb9b77e..d2d698713 100644 --- a/unix-cpy/Makefile +++ b/unix-cpy/Makefile @@ -1,95 +1,37 @@ -PYSRC=../py -BUILD=build +# define main target +PROG = cpy +all: $(PROG) +# include py core make definitions +include ../py/py.mk + +# program for deletion +RM = /bin/rm + +# compiler settings CC = gcc -CFLAGS = -I. -I$(PYSRC) -Wall -Werror -ansi -std=gnu99 -Os #-DNDEBUG +CFLAGS = -I. -I$(PY_SRC) -Wall -Werror -ansi -std=gnu99 -Os #-DNDEBUG LDFLAGS = -lm +# source files SRC_C = \ main.c \ -PY_O = \ - nlrx86.o \ - nlrx64.o \ - malloc.o \ - qstr.o \ - vstr.o \ - unicode.o \ - lexer.o \ - lexerunix.o \ - parse.o \ - scope.o \ - compile.o \ - emitcommon.o \ - emitpass1.o \ - emitcpy.o \ - runtime.o \ - map.o \ - obj.o \ - objbool.o \ - objboundmeth.o \ - objcell.o \ - objclass.o \ - objclosure.o \ - objcomplex.o \ - objdict.o \ - objexcept.o \ - objfloat.o \ - objfun.o \ - objgenerator.o \ - objinstance.o \ - objint.o \ - objlist.o \ - objmodule.o \ - objnone.o \ - objrange.o \ - objset.o \ - objslice.o \ - objstr.o \ - objtuple.o \ - objtype.o \ - builtin.o \ - builtinimport.o \ - vm.o \ - showbc.o \ - repl.o \ - -OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(PY_O)) +OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) $(PY_O) LIB = -PROG = cpy $(PROG): $(BUILD) $(OBJ) $(CC) -o $@ $(OBJ) $(LIB) $(LDFLAGS) - -$(BUILD): - mkdir -p $@ + strip $(PROG) + size $(PROG) $(BUILD)/%.o: %.c $(CC) $(CFLAGS) -c -o $@ $< -$(BUILD)/%.o: $(PYSRC)/%.S - $(CC) $(CFLAGS) -c -o $@ $< - -$(BUILD)/%.o: $(PYSRC)/%.c mpconfigport.h - $(CC) $(CFLAGS) -c -o $@ $< - -$(BUILD)/emitnx64.o: $(PYSRC)/emitnative.c $(PYSRC)/emit.h - $(CC) $(CFLAGS) -DN_X64 -c -o $@ $< - -$(BUILD)/emitnthumb.o: $(PYSRC)/emitnative.c $(PYSRC)/emit.h - $(CC) $(CFLAGS) -DN_THUMB -c -o $@ $< - -# optimising vm for speed, adds only a small amount to code size but makes a huge difference to speed (20% faster) -$(BUILD)/vm.o: $(PYSRC)/vm.c - $(CC) $(CFLAGS) -O3 -c -o $@ $< - $(BUILD)/main.o: mpconfigport.h -$(BUILD)/parse.o: $(PYSRC)/grammar.h -$(BUILD)/compile.o: $(PYSRC)/grammar.h -$(BUILD)/emitcpy.o: $(PYSRC)/emit.h -$(BUILD)/emitbc.o: $(PYSRC)/emit.h clean: - /bin/rm -rf $(BUILD) + $(RM) -f $(PROG) + $(RM) -rf $(BUILD) -.PHONY: clean +.PHONY: all clean diff --git a/unix-cpy/main.c b/unix-cpy/main.c index eba97f527..ea85e3275 100644 --- a/unix-cpy/main.c +++ b/unix-cpy/main.c @@ -11,7 +11,6 @@ #include "obj.h" #include "compile.h" #include "runtime0.h" -#include "runtime.h" void do_file(const char *file) { mp_lexer_t *lex = mp_lexer_new_from_file(file); diff --git a/unix-cpy/mpconfigport.h b/unix-cpy/mpconfigport.h index 983b166a5..3fc186677 100644 --- a/unix-cpy/mpconfigport.h +++ b/unix-cpy/mpconfigport.h @@ -1,10 +1,8 @@ // options to control how Micro Python is built -#define MICROPY_ENABLE_FLOAT (1) #define MICROPY_EMIT_CPYTHON (1) -#define MICROPY_EMIT_X64 (0) -#define MICROPY_EMIT_THUMB (0) -#define MICROPY_EMIT_INLINE_THUMB (0) +#define MICROPY_ENABLE_LEXER_UNIX (1) +#define MICROPY_ENABLE_FLOAT (1) // type definitions for the specific machine diff --git a/unix/Makefile b/unix/Makefile index 984f1c3bf..a3faea290 100644 --- a/unix/Makefile +++ b/unix/Makefile @@ -1,106 +1,39 @@ -PYSRC=../py -BUILD=build +# define main target +PROG = py +all: $(PROG) +# include py core make definitions +include ../py/py.mk + +# program for deletion +RM = /bin/rm + +# compiler settings CC = gcc -CFLAGS = -I. -I$(PYSRC) -Wall -Werror -ansi -std=gnu99 -Os #-DNDEBUG +CFLAGS = -I. -I$(PY_SRC) -Wall -Werror -ansi -std=gnu99 -Os #-DNDEBUG LDFLAGS = -lm +# source files SRC_C = \ main.c \ -PY_O = \ - nlrx86.o \ - nlrx64.o \ - nlrthumb.o \ - malloc.o \ - qstr.o \ - vstr.o \ - unicode.o \ - lexer.o \ - lexerunix.o \ - parse.o \ - scope.o \ - compile.o \ - emitcommon.o \ - emitpass1.o \ - emitcpy.o \ - emitbc.o \ - asmx64.o \ - emitnx64.o \ - asmthumb.o \ - emitnthumb.o \ - emitinlinethumb.o \ - runtime.o \ - map.o \ - obj.o \ - objbool.o \ - objboundmeth.o \ - objcell.o \ - objclass.o \ - objclosure.o \ - objcomplex.o \ - objdict.o \ - objexcept.o \ - objfloat.o \ - objfun.o \ - objgenerator.o \ - objinstance.o \ - objint.o \ - objlist.o \ - objmodule.o \ - objnone.o \ - objrange.o \ - objset.o \ - objslice.o \ - objstr.o \ - objtuple.o \ - objtype.o \ - builtin.o \ - builtinimport.o \ - vm.o \ - showbc.o \ - repl.o \ - -OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(PY_O)) +OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) $(PY_O) LIB = -lreadline # the following is needed for BSD #LIB += -ltermcap -PROG = py $(PROG): $(BUILD) $(OBJ) $(CC) -o $@ $(OBJ) $(LIB) $(LDFLAGS) strip $(PROG) size $(PROG) -$(BUILD): - mkdir -p $@ - $(BUILD)/%.o: %.c $(CC) $(CFLAGS) -c -o $@ $< -$(BUILD)/%.o: $(PYSRC)/%.S - $(CC) $(CFLAGS) -c -o $@ $< - -$(BUILD)/%.o: $(PYSRC)/%.c mpconfigport.h - $(CC) $(CFLAGS) -c -o $@ $< - -$(BUILD)/emitnx64.o: $(PYSRC)/emitnative.c $(PYSRC)/emit.h mpconfigport.h - $(CC) $(CFLAGS) -DN_X64 -c -o $@ $< - -$(BUILD)/emitnthumb.o: $(PYSRC)/emitnative.c $(PYSRC)/emit.h mpconfigport.h - $(CC) $(CFLAGS) -DN_THUMB -c -o $@ $< - -# optimising vm for speed, adds only a small amount to code size but makes a huge difference to speed (20% faster) -$(BUILD)/vm.o: $(PYSRC)/vm.c - $(CC) $(CFLAGS) -O3 -c -o $@ $< - $(BUILD)/main.o: mpconfigport.h -$(BUILD)/parse.o: $(PYSRC)/grammar.h -$(BUILD)/compile.o: $(PYSRC)/grammar.h -$(BUILD)/emitcpy.o: $(PYSRC)/emit.h -$(BUILD)/emitbc.o: $(PYSRC)/emit.h clean: - /bin/rm -rf $(BUILD) + $(RM) -f $(PROG) + $(RM) -rf $(BUILD) -.PHONY: clean +.PHONY: all clean diff --git a/unix/main.c b/unix/main.c index a06dc3679..920aed344 100644 --- a/unix/main.c +++ b/unix/main.c @@ -20,6 +20,52 @@ #include #endif +static void execute_from_lexer(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, bool is_repl) { + if (lex == NULL) { + return; + } + + if (0) { + // just tokenise + while (!mp_lexer_is_kind(lex, MP_TOKEN_END)) { + mp_token_show(mp_lexer_cur(lex)); + mp_lexer_to_next(lex); + } + mp_lexer_free(lex); + return; + } + + mp_parse_node_t pn = mp_parse(lex, input_kind); + mp_lexer_free(lex); + + if (pn == MP_PARSE_NODE_NULL) { + // parse error + return; + } + + //printf("----------------\n"); + //parse_node_show(pn, 0); + //printf("----------------\n"); + + mp_obj_t module_fun = mp_compile(pn, is_repl); + + if (module_fun == mp_const_none) { + // compile error + return; + } + + // execute it + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + rt_call_function_0(module_fun); + nlr_pop(); + } else { + // uncaught exception + mp_obj_print((mp_obj_t)nlr.ret_val); + printf("\n"); + } +} + static char *str_join(const char *s1, int sep_char, const char *s2) { int l1 = strlen(s1); int l2 = strlen(s2); @@ -80,28 +126,11 @@ static void do_repl(void) { } mp_lexer_t *lex = mp_lexer_new_from_str_len("", line, strlen(line), false); - mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT); - mp_lexer_free(lex); - - if (pn != MP_PARSE_NODE_NULL) { - //mp_parse_node_show(pn, 0); - mp_obj_t module_fun = mp_compile(pn, true); - if (module_fun != mp_const_none) { - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - rt_call_function_0(module_fun); - nlr_pop(); - } else { - // uncaught exception - mp_obj_print((mp_obj_t)nlr.ret_val); - printf("\n"); - } - } - } + execute_from_lexer(lex, MP_PARSE_SINGLE_INPUT, true); } } -void do_file(const char *file) { +static void do_file(const char *file) { // hack: set dir for import based on where this file is { const char * s = strrchr(file, '/'); @@ -115,58 +144,17 @@ void do_file(const char *file) { } mp_lexer_t *lex = mp_lexer_new_from_file(file); - //const char *pysrc = "def f():\n x=x+1\n print(42)\n"; - //mp_lexer_t *lex = mp_lexer_from_str_len("<>", pysrc, strlen(pysrc), false); - if (lex == NULL) { - return; - } + execute_from_lexer(lex, MP_PARSE_FILE_INPUT, false); +} - if (0) { - // just tokenise - while (!mp_lexer_is_kind(lex, MP_TOKEN_END)) { - mp_token_show(mp_lexer_cur(lex)); - mp_lexer_to_next(lex); - } - mp_lexer_free(lex); - - } else { - // compile - - mp_parse_node_t pn = mp_parse(lex, MP_PARSE_FILE_INPUT); - mp_lexer_free(lex); - - if (pn != MP_PARSE_NODE_NULL) { - //printf("----------------\n"); - //parse_node_show(pn, 0); - //printf("----------------\n"); - mp_obj_t module_fun = mp_compile(pn, false); - //printf("----------------\n"); - -#if MICROPY_EMIT_CPYTHON - if (!comp_ok) { - printf("compile error\n"); - } -#else - if (1 && module_fun != mp_const_none) { - // execute it - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - rt_call_function_0(module_fun); - nlr_pop(); - } else { - // uncaught exception - mp_obj_print((mp_obj_t)nlr.ret_val); - printf("\n"); - } - } -#endif - } - } +static void do_str(const char *str) { + mp_lexer_t *lex = mp_lexer_new_from_str_len("", str, strlen(str), false); + execute_from_lexer(lex, MP_PARSE_SINGLE_INPUT, false); } typedef struct _test_obj_t { mp_obj_base_t base; - bool value; + int value; } test_obj_t; static void test_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) { @@ -188,21 +176,17 @@ static mp_obj_t test_set(mp_obj_t self_in, mp_obj_t arg) { static MP_DEFINE_CONST_FUN_OBJ_1(test_get_obj, test_get); static MP_DEFINE_CONST_FUN_OBJ_2(test_set_obj, test_set); +static const mp_method_t test_methods[] = { + { "get", &test_get_obj }, + { "set", &test_set_obj }, + { NULL, NULL }, +}; + static const mp_obj_type_t test_type = { { &mp_const_type }, "Test", .print = test_print, - .make_new = NULL, - .call_n = NULL, - .unary_op = NULL, - .binary_op = NULL, - .getiter = NULL, - .iternext = NULL, - .methods = { - { "get", &test_get_obj }, - { "set", &test_set_obj }, - { NULL, NULL }, - } + .methods = test_methods, }; mp_obj_t test_obj_new(int value) { @@ -212,6 +196,11 @@ mp_obj_t test_obj_new(int value) { return o; } +int usage(void) { + printf("usage: py [-c ] []\n"); + return 1; +} + int main(int argc, char **argv) { qstr_init(); rt_init(); @@ -227,12 +216,24 @@ int main(int argc, char **argv) { if (argc == 1) { do_repl(); - } else if (argc == 2) { - do_file(argv[1]); } else { - printf("usage: py []\n"); - return 1; + for (int a = 1; a < argc; a++) { + if (argv[a][0] == '-') { + if (strcmp(argv[a], "-c") == 0) { + if (a + 1 >= argc) { + return usage(); + } + do_str(argv[a + 1]); + a += 1; + } else { + return usage(); + } + } else { + do_file(argv[a]); + } + } } + rt_deinit(); //printf("total bytes = %d\n", m_get_total_bytes_allocated()); diff --git a/unix/mpconfigport.h b/unix/mpconfigport.h index 7a4622b8b..832764121 100644 --- a/unix/mpconfigport.h +++ b/unix/mpconfigport.h @@ -5,11 +5,13 @@ #define MICROPY_USE_READLINE (1) #endif -#define MICROPY_ENABLE_FLOAT (1) -#define MICROPY_EMIT_CPYTHON (0) #define MICROPY_EMIT_X64 (1) #define MICROPY_EMIT_THUMB (0) #define MICROPY_EMIT_INLINE_THUMB (0) +#define MICROPY_MEM_STATS (1) +#define MICROPY_ENABLE_REPL_HELPERS (1) +#define MICROPY_ENABLE_LEXER_UNIX (1) +#define MICROPY_ENABLE_FLOAT (1) // type definitions for the specific machine From baa665406fef58cfb0be0df8d2798ed0961506da Mon Sep 17 00:00:00 2001 From: "John R. Lenton" Date: Tue, 7 Jan 2014 23:18:25 +0000 Subject: [PATCH 18/18] Moved dict methods out to a mp_method_t. --- py/objdict.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/py/objdict.c b/py/objdict.c index 7cfd597ed..c36d4ccb0 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -102,7 +102,6 @@ static const mp_obj_type_t dict_it_type = { { &mp_const_type }, "dict_iterator", .iternext = dict_it_iternext, - .methods = { { NULL, NULL }, }, }; static mp_obj_t mp_obj_new_dict_iterator(mp_obj_dict_t *dict, int cur) { @@ -250,6 +249,17 @@ static MP_DEFINE_CONST_FUN_OBJ_2(dict_update_obj, dict_update); /******************************************************************************/ /* dict constructors & etc */ +static const mp_method_t dict_type_methods[] = { + { "clear", &dict_clear_obj }, + { "copy", &dict_copy_obj }, + { "get", &dict_get_obj }, + { "pop", &dict_pop_obj }, + { "popitem", &dict_popitem_obj }, + { "setdefault", &dict_setdefault_obj }, + { "update", &dict_update_obj }, + { NULL, NULL }, // end-of-list sentinel +}; + const mp_obj_type_t dict_type = { { &mp_const_type }, "dict", @@ -257,16 +267,7 @@ const mp_obj_type_t dict_type = { .make_new = dict_make_new, .binary_op = dict_binary_op, .getiter = dict_getiter, - .methods = { - { "clear", &dict_clear_obj }, - { "copy", &dict_copy_obj }, - { "get", &dict_get_obj }, - { "pop", &dict_pop_obj }, - { "popitem", &dict_popitem_obj }, - { "setdefault", &dict_setdefault_obj }, - { "update", &dict_update_obj }, - { NULL, NULL }, // end-of-list sentinel - }, + .methods = dict_type_methods, }; mp_obj_t mp_obj_new_dict(int n_args) {